krz/vox-populi

clone: git clone https://gitbay.org/krz/vox-populi.git

main: search.php · raw

  1<?php
  2    require __DIR__ . '/vendor/autoload.php';
  3
  4    session_start();
  5
  6    $consumer_key = getenv('CONSUMER_KEY');
  7    $consumer_secret = getenv('CONSUMER_SECRET');
  8    $client = new Tumblr\API\Client($consumer_key, $consumer_secret);
  9    $requestHandler = $client->getRequestHandler();
 10    $requestHandler->setBaseUrl('https://www.tumblr.com/');
 11
 12    // Check if the user has already authenticated
 13    if(isset($_SESSION['perm_token']) && !empty($_SESSION['perm_token']) && isset($_SESSION['perm_secret']) && !empty($_SESSION['perm_secret'])) {
 14        $token = $_SESSION['perm_token'];
 15        $token_secret = $_SESSION['perm_secret'];
 16    }
 17    // Check if the user was here earlier by checking cookies
 18    else if (isset($_COOKIE['perm_token']) && !empty($_COOKIE['perm_token']) && isset($_COOKIE['perm_secret']) && !empty($_COOKIE['perm_secret'])) {
 19        $token = $_COOKIE['perm_token'];
 20        $token_secret = $_COOKIE['perm_secret'];
 21    }
 22    // Check if this is the user's first visit
 23    else if (!isset($_GET['oauth_verifier'])) {
 24
 25        // Grab the oauth token
 26        $resp = $requestHandler->request('POST', 'oauth/request_token', array());
 27        $out = $result = $resp->body;
 28        $data = array();
 29        parse_str($out, $data);
 30
 31        // Save temporary tokens to session
 32        $_SESSION['tmp_token'] = $data['oauth_token'];
 33        $_SESSION['tmp_secret'] = $data['oauth_token_secret'];
 34
 35        // Redirect user to Tumblr auth page
 36        session_regenerate_id(true);
 37        $header_url = 'https://www.tumblr.com/oauth/authorize?oauth_token=' . $data['oauth_token'];
 38        header('Location: ' . $header_url);
 39        die();
 40
 41    }
 42    // Check if the user was just sent back from the Tumblr authentication site
 43    else {
 44
 45        $verifier = $_GET['oauth_verifier'];
 46
 47        // Use the stored temporary tokens
 48        $client->setToken($_SESSION['tmp_token'], $_SESSION['tmp_secret']);
 49
 50        // Access the permanent tokens
 51        $resp = $requestHandler->request('POST', 'oauth/access_token', array('oauth_verifier' => $verifier));
 52        $out = $result = $resp->body;
 53        $data = array();
 54        parse_str($out, $data);
 55
 56        // Set permanent tokens
 57        $token = $data['oauth_token'];
 58        $token_secret = $data['oauth_token_secret'];;
 59        $_SESSION['perm_token'] = $data['oauth_token'];
 60        $_SESSION['perm_secret'] = $data['oauth_token_secret'];
 61
 62        // Set cookies in case the user comes back later
 63        setcookie("perm_token", $_SESSION['perm_token']);
 64        setcookie("perm_secret", $_SESSION['perm_secret']);
 65
 66        // Redirect user to homepage for a clean URL
 67        session_regenerate_id(true);
 68        $header_url = 'https://example.com/vox-populi/';
 69        header('Location: ' . $header_url);
 70        die();
 71
 72    }
 73
 74    // Authenticate via OAuth
 75    $client = new Tumblr\API\Client(
 76        $consumer_key,
 77        $consumer_secret,
 78        $token,
 79        $token_secret
 80    );
 81
 82    // Set up a function to check if variables are blank later (this function accepts 0, 0.0, and "0" as valid)
 83    function not_blank($value) {
 84        return !empty($value) && isset($value) && $value !== '';
 85    }
 86
 87    // Echo HTML contents
 88    echo '<!doctype html><html lang="en"><head>
 89    <meta charset="utf-8">
 90    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 91    <meta http-equiv="x-ua-compatible" content="ie=edge">
 92    <meta name="robots" content="none">
 93    <link rel="apple-touch-icon" sizes="180x180" href="./assets/favicon/apple-touch-icon.png">
 94    <link rel="icon" type="image/png" sizes="32x32" href="./assets/favicon/favicon-32x32.png">
 95    <link rel="icon" type="image/png" sizes="16x16" href="./assets/favicon/favicon-16x16.png">
 96    <link rel="manifest" href="./assets/favicon/site.webmanifest">
 97    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
 98        integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
 99    <link rel="stylesheet"  href="./assets/css/app.css">
100    <title>Vox Populi - A Tumblr Web Client</title>
101    <style>
102        @font-face {
103            font-family: "IBM Plex Sans";
104            src: url("./assets/fonts/IBMPlexSans-Regular.eot");
105            src: url("./assets/fonts/IBMPlexSans-Regular.woff2") format("woff2"),
106                url("./assets/fonts/IBMPlexSans-Regular.woff") format("woff"),
107                url("./assets/fonts/IBMPlexSans-Regular.ttf") format("truetype");
108        }
109
110        @font-face {
111            font-family: "IBM Plex Sans";
112            src: url("./assets/fonts/IBMPlexSans-Bold.eot");
113            src: url("./assets/fonts/IBMPlexSans-Bold.woff2") format("woff2"),
114                url("./assets/fonts/IBMPlexSans-Bold.woff") format("woff"),
115                url("./assets/fonts/IBMPlexSans-Bold.ttf") format("truetype");
116            font-weight: bold;
117        }
118
119        @font-face {
120            font-family: "IBM Plex Mono";
121            src: url("./assets/fonts/IBMPlexMono-Regular.eot");
122            src: url("./assets/fonts/IBMPlexMono-Regular.woff2") format("woff2"),
123                url("./assets/fonts/IBMPlexMono-Regular.woff") format("woff"),
124                url("./assets/fonts/IBMPlexMono-Regular.ttf") format("truetype");
125        }
126
127        @font-face {
128            font-family: "IBM Plex Mono";
129            src: url("./assets/fonts/IBMPlexMono-Bold.eot");
130            src: url("./assets/fonts/IBMPlexMono-Bold.woff2") format("woff2"),
131                url("./assets/fonts/IBMPlexMono-Bold.woff") format("woff"),
132                url("./assets/fonts/IBMPlexMono-Bold.ttf") format("truetype");
133            font-weight: bold;
134        }
135    </style>
136</head>
137
138<body>
139    <div class="banner fixed-top" role="contentinfo">
140        <p class="m-0 mr-2">Black Lives Matter.</p>
141        <a href="https://support.eji.org/give/153413/#!/donation/checkout">Support the <span
142                class="d-none d-md-inline">Equal Justice Initiative</span><span
143                class="d-inline d-md-none">EJI</span></a>
144    </div>
145    <nav class="navbar navbar-expand-lg navbar-dark fixed-top">
146        <li class="nav-item d-block d-md-none">
147            <a id="sidebarButton" class="nav-link" href="javascript:void(0)"><svg id="icon-side-menu"
148                    xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32" aria-hidden="true">
149                    <path
150                        d="M14 4H18V8H14zM4 4H8V8H4zM24 4H28V8H24zM14 14H18V18H14zM4 14H8V18H4zM24 14H28V18H24zM14 24H18V28H14zM4 24H8V28H4zM24 24H28V28H24z">
151                    </path>
152                </svg></a>
153        </li>
154        <a class="navbar-brand" href="./">Vox Populi</a>
155
156        <ul class="navbar-nav mr-0 ml-auto flex-row">
157            <li class="nav-item">
158                <a id="search-button" class="nav-link" href="javascript:void(0)"><svg id="icon-search"
159                        xmlns="http://www.w3.org/2000/svg" description="Open search" width="20" height="20"
160                        viewBox="0 0 32 32" aria-hidden="true">
161                        <path d="M30,28.59,22.45,21A11,11,0,1,0,21,22.45L28.59,30ZM5,14a9,9,0,1,1,9,9A9,9,0,0,1,5,14Z">
162                        </path>
163                    </svg></a>
164            </li>
165        </ul>
166        <form id="search-form" class="form-inline h-100 d-none flex-fill" method="get"
167            action="./search.php">
168            <input class="form-control h-100 ml-auto" type="search" placeholder="Search" aria-label="Search"
169                name="query">
170            <a id="search-close-button" class="nav-link" href="javascript:void(0)">
171                <svg id="icon-search" xmlns="http://www.w3.org/2000/svg" description="Clear search" width="20"
172                    height="20" viewBox="0 0 32 32" aria-hidden="true">
173                    <path
174                        d="M24 9.4L22.6 8 16 14.6 9.4 8 8 9.4 14.6 16 8 22.6 9.4 24 16 17.4 22.6 24 24 22.6 17.4 16 24 9.4z">
175                    </path>
176                </svg>
177            </a>
178        </form>
179    </nav>
180
181    <div class="container-fluid">
182        <div class="row">
183            <div id="sidebar" class="sidebar d-none d-md-block col-md-3 col-lg-2 p-0">
184                <div class="sidebar-box p-0">
185                    <div>
186                        <ul class="actions">
187                            <li><p>Account</p></li>
188                            <hr class="sidebar-divider">
189                            <li><a href="./profile/">Profile</a></li>
190                            <li><a href="./logout.php">Logout</a></li>
191                        </ul>
192                        <ul class="categories">
193                            <li><p>Filter Posts</p></li>
194                            <hr class="sidebar-divider">
195                            <li><a href="./">All Posts</a></li>
196                            <li><a href="./?type=answer">Answer</a></li>
197                            <li><a href="./?type=audio">Audio</a></li>
198                            <li><a href="./?type=chat">Chat</a></li>
199                            <li><a href="./?type=link">Link</a></li>
200                            <li><a href="./?type=photo">Photo</a></li>
201                            <li><a href="./?type=quote">Quote</a></li>
202                            <li><a href="./?type=text">Text</a></li>
203                            <li><a href="./?type=video">Video</a></li>
204                        </ul>
205                    </div>
206                    <div>
207                        <hr class="sidebar-divider">
208                        <ul class="meta">
209                            <li>
210                            <a href="#" target="_blank">Source Code <svg
211                                        id="icon-external-link" xmlns="http://www.w3.org/2000/svg" width="16"
212                                        height="16" viewBox="0 0 16 16" aria-hidden="true">
213                                        <path
214                                            d="M13,14H3c-0.6,0-1-0.4-1-1V3c0-0.6,0.4-1,1-1h5v1H3v10h10V8h1v5C14,13.6,13.6,14,13,14z">
215                                        </path>
216                                        <path d="M10 1L10 2 13.3 2 9 6.3 9.7 7 14 2.7 14 6 15 6 15 1z"></path>
217                                    </svg>
218                                </a>
219                            </li>
220                            <li>
221                                <a href="https://example.com" target="_blank">&copy; 0000</a>
222                            </li>
223                        </ul>
224                    </div>
225                </div>
226            </div>
227            <div id="main" class="main col-xs-12 col-md-9 col-lg-10 ml-auto">
228                <span class="homepage--dots"></span>';
229
230            // Get the user's blog name for welcome message
231            $client->getUserInfo();
232            foreach ($client->getUserInfo()->user->blogs as $blog) {
233                echo '<h1 class="text-center py-4">Search results for: <span class="search-query">' . $_GET['query'] . '</span></h1>';
234            }
235
236            // Create function to allow a client to get 20 posts per page
237            function get_tagged_posts($client, $before_date, $limit) {
238                $tag = $_GET['query'];
239                $tagged_posts = $client->getTaggedPosts($tag, $options=null);
240                $card_columns = '<div class="card-columns">';
241                foreach ($tagged_posts as $post) {
242                    $card_columns .= '<div class="card" data-type="' . $post->type . '" data-id="' . $post->id_string . '">';
243                    $card_columns .= '<div class="card-header d-flex justify-content-between"><div class="card-header-blog"><a href="' . $post->blog->url . '" target="_blank"><img class="avatar" src="' . $client->getBlogAvatar($post->blog_name, 32) . '"></a>';
244                    $card_columns .= '<a href="' . $post->blog->url . '">' . $post->blog_name . '</a></div>';
245
246                    if ($post->followed != true) {
247                        // Send user to action.php to follow a new blog
248                        $query_string = 'action=follow&blog_name=' . urlencode($post->blog_name) . '&callback=' . urlencode('https://' . $_SERVER[HTTP_HOST] . $_SERVER[REQUEST_URI]);
249                        $card_columns .= '<a href="action.php?' . htmlentities($query_string) . '"><i data-feather="user-plus"></i></a></div>';
250                    } else {
251                        $card_columns .= '</div>';
252                    }
253
254                    if ($post->type == 'photo') {
255                        $card_columns .= '<a href="' . $post->photos[0]->original_size->url . '"><img src="' . $post->photos[0]->original_size->url . '" class="card-img" alt="..."></a>';
256                    }
257                    if ($post->type == 'video') {
258                        $card_columns .= '<video controls poster="'.$post->thumbnail_url.'" class="card-img"><source src="'.$post->video_url.'"></source></video>';
259                    }
260                    $card_columns .= '<div class="card-body">';
261                    if ($post->type == 'photo') {
262                        if (not_blank($post->caption)) {
263                            $card_columns .= '<p class="card-text post-caption">' . $post->caption . '</p>';
264                        }
265                    }
266                    if ($post->type == 'text') {
267                        if (not_blank($post->title)) {
268                            $card_columns .= '<h5 class="card-title post-title">' . $post->title . '</h5>';
269                        }
270                        if (not_blank($post->body)) {
271                            $card_columns .= '<p class="card-text post-body">' . $post->body . '</p>';
272                        }
273                    }
274                    if ($post->type == 'answer') {
275                        $card_columns .= '<p class="card-text"><b>' . $post->asking_name . '</b>: ' . $post->question . '</p>';
276                        $card_columns .= '<hr>';
277                        $card_columns .= $post->answer;
278                    }
279                    if ($post->type == 'quote') {
280                        $card_columns .= '<blockquote class="card-text post-quote">' . $post->summary;
281                        $card_columns .= '<br><br><cite>' . $post->reblog->comment . '</cite></blockquote>';
282                    }
283                    if ($post->type == 'chat') {
284                        for ($i = 0; $i <= count($post->dialogue); $i++) {
285                            $card_columns .= '<blockquote class="card-text post-chat-' . $i . '">' . (not_blank($post->dialogue[$i]->name) ? ('<b>' . $post->dialogue[$i]->name . '</b>: ') : "") . $post->dialogue[$i]->phrase . '</blockquote>';
286                        }
287                    }
288                    /* This seems to just post a duplicate of a QA answer
289                    if (isset($post->reblog->comment) && $post->reblog->comment !== '') {
290                        $card_columns .= '<p class="card-text reblog-comment">' . $post->reblog->comment . '</p>';
291                    }
292                    */
293                    // $card_columns .= '<a href="' . $post->post_url . '" class="card-link">Visit Post &rarr;</a>';
294                    $card_columns .= '<div class="card-footer d-flex justify-content-between align-items-center p-0"><div class="note-count">' . number_format($post->note_count, 0) . ' notes</div>';
295                    $card_columns .= '<div class="post-icons"><a href="' . $post->post_url . '" target="_blank" title="View on Tumblr"><i data-feather="external-link"></i></a>';
296                    $card_columns .= '<a href="#" title="Share"><i data-feather="send"></i></a>';
297                    $card_columns .= '<a href="#" title="Comment"><i data-feather="message-square"></i></a>';
298                    $card_columns .= '<a href="#" title="Reblog"><i data-feather="repeat"></i></a>';
299                    $card_columns .= '<a href="#" title="Like"><i data-feather="heart"></i></a></div></div>';
300                    $card_columns .= '</div></div>';
301                }
302                $card_columns .= '</div>';
303                return $card_columns;
304            }
305
306            // Create a loop to call as many dashboard posts as you want (results are returned in sets of 20 per API rules)
307            // Can specify post type: text, chat, link, photo, audio, video, NULL
308            if (isset($_GET['type'])) {
309                $post_type = $_GET['type'];
310            } else {
311                $post_type = NULL;
312            }
313            if (isset($_GET['page'])) {
314                $page = $_GET['page'];
315            } else {
316                $page = 1;
317            }
318            $post_start = (($page - 1) * 20) + 1;
319            $limit = 20;
320            echo get_tagged_posts($client, $post_start, $limit, $post_type);
321
322    echo '</div></div></div>
323            <button class="btn-to-top" type="button" aria-label="Back to Top">
324            <svg id="icon-to-top" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 32 32"
325                aria-hidden="true">
326                <path d="M16 14L6 24 7.4 25.4 16 16.8 24.6 25.4 26 24zM4 8H28V10H4z"></path>
327            </svg>
328            </button>
329
330            <!-- JavaScript -->
331            <script src="https://unpkg.com/feather-icons"></script>
332            <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
333            <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
334            <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
335            <script src="./assets/js/app.js"></script>
336            </body></html>';
337?>