/** * Batch import an array of articles * * @param array $articles Associative array of the articles * @return string */ public function batchImport($articles) { # Loop the articles foreach ($articles as $i => $article) { # Validate the article's array keys if (!\Kanso\Utility\Arr::issets(['created', 'modified', 'status', 'type', 'title', 'excerpt', 'category', 'tags', 'content', 'thumbnail', 'comments_enabled'], $article)) { return "invalid_json"; } if (!is_numeric($article['created'])) { return "invalid_json"; } if (!is_numeric($article['modified'])) { return "invalid_json"; } if ($article['type'] !== 'page' && $article['type'] !== 'post') { return "invalid_json"; } if ($article['status'] !== 'published' && $article['status'] !== 'draft') { return "invalid_json"; } # Sanitize values $articles[$i]['title'] = filter_var($articles[$i]['title'], FILTER_SANITIZE_STRING); $articles[$i]['excerpt'] = filter_var($articles[$i]['excerpt'], FILTER_SANITIZE_STRING); $articles[$i]['category'] = filter_var($articles[$i]['category'], FILTER_SANITIZE_STRING); $articles[$i]['thumbnail'] = filter_var($articles[$i]['thumbnail'], FILTER_SANITIZE_STRING); $articles[$i]['tags'] = filter_var($articles[$i]['tags'], FILTER_SANITIZE_STRING); $article['comments_enabled'] = (bool) $article['comments_enabled']; $articles[$i]['created'] = (int) $articles[$i]['created']; $articles[$i]['modified'] = (int) $articles[$i]['modified']; if (isset($articles[$i]['author_id'])) { $articles[$i]['author_id'] = (int) $articles[$i]['author_id']; } $post = $this->create(); foreach ($articles[$i] as $key => $value) { $post->{$key} = $value; } if (!$post->save()) { return "invalid_json"; } } return true; }
/** * Build HTML Pagination links * * @param array $args Associative array of options (optional) */ public function pagination_links($args = null) { # Default options $options = ['base' => \Kanso\Kanso::getInstance()->Environment()['HTTP_HOST'], 'format' => '<li class="(:class)"><a href="(:link)">(:num)</a></li>', 'current' => 1, 'total' => 1, 'context' => 2, 'show_all' => false, 'prev_next' => true, 'ellipsis' => '<li>. . .</li>', 'prev_text' => '« Previous', 'next_text' => 'Next »']; # Segment the reuest URI $uri = explode("/", trim(\Kanso\Kanso::getInstance()->Environment()['PATH_INFO'], '/')); # Declare the pagination string $pagination = ''; # Replace the query and create a new one to get the post count; $queryStr = preg_replace('/limit =[^:]+:/', '', $this->queryStr); $queryStr = trim(preg_replace('/limit =[^:]+/', '', $queryStr)); $queryStr = trim($queryStr, ":"); # Load the query parser $parser = new QueryParser(); # Count the posts $posts = $parser->countQuery($queryStr); $pages = \Kanso\Utility\Arr::paginate($posts, $this->pageIndex, \Kanso\Kanso::getInstance()->Config()['KANSO_POSTS_PER_PAGE']); # If no args were defined, Kanso will figure it out for us if (!$args || !isset($args['current']) || !isset($args['total'])) { # pages here are used as for an array so +1 $options['current'] = $this->pageIndex === 0 ? 1 : $this->pageIndex + 1; $options['total'] = count($pages); } # If options were set, overwrite the dafaults if ($args) { $options = array_merge($options, $args); } # Special case if there is only 1 page if ($options['total'] == 1 || $options['total'] == 0 || $options['total'] < 1) { return ''; } # Clean the base url $options['base'] = rtrim($options['base'], '/'); # Update the base url depending on the page type if ($this->is_search()) { $options['base'] = $options['base'] . DIRECTORY_SEPARATOR . 'search-results/?q=' . $this->searchQuery; } else { if ($this->is_archive()) { $options['base'] = $options['base'] . DIRECTORY_SEPARATOR . 'archive'; } else { if ($this->is_tag()) { $options['base'] = $options['base'] . DIRECTORY_SEPARATOR . $uri[0] . DIRECTORY_SEPARATOR . $uri[1]; } else { if ($this->is_category()) { $options['base'] = $options['base'] . DIRECTORY_SEPARATOR . $uri[0] . DIRECTORY_SEPARATOR . $uri[1]; } else { if ($this->is_author()) { $options['base'] = $options['base'] . DIRECTORY_SEPARATOR . $uri[0] . DIRECTORY_SEPARATOR . $uri[1]; } } } } } # loop always at the current minus the context, minus 1 $loopStart = $options['current'] - $options['context']; # if the loop starts before 2, reset it to 2 if ($loopStart < 2) { $loopStart = 2; } # Loop end is the context * 2 + loop start + plus 1 $loopEnd = $loopStart + $options['context'] * 2 + 1; # We should show all links if the loop ends after the total if ($loopEnd >= $options['total'] || $options['show_all'] === true) { $loopEnd = $options['total']; } # Declare variables we are going to use $frontEllipsis = $loopStart > 2 ? $options['ellipsis'] : ''; $backEllipsis = $loopEnd === $options['total'] || $options['total'] - $options['context'] === $loopEnd ? '' : $options['ellipsis']; # Variables we will need $patterns = ['/\\(:class\\)/', '/\\(:link\\)/', '/\\(:num\\)/']; $replacements = []; # If show all is true we need reset if ($options['show_all'] === true) { $frontEllipsis = ''; $backEllipsis = ''; $loopStart = 2; $loopEnd = $options['total']; } # If show previous if ($options['prev_next'] === true) { $class = $options['current'] === 1 ? 'disabled' : ''; $link = $options['current'] === 1 ? '#' : $options['base'] . DIRECTORY_SEPARATOR . 'page' . DIRECTORY_SEPARATOR . ($options['current'] - 1) . DIRECTORY_SEPARATOR; $link = $options['current'] === 2 ? $options['base'] : $link; $replacements = [$class, $link, $options['prev_text']]; $pagination .= preg_replace($patterns, $replacements, $options['format']); $replacements = []; } # Show the first page $class = $options['current'] === 1 ? 'active' : ''; $link = $options['current'] === 1 ? '#' : $options['base']; $replacements = [$class, $link, 1]; $pagination .= preg_replace($patterns, $replacements, $options['format']); $replacements = []; # Show the front ellipsis $pagination .= $frontEllipsis; # Loop over the pages # Note the loop starts after the first page and before the last page for ($i = $loopStart; $i < $loopEnd; $i++) { $class = $i === $options['current'] ? 'active' : ''; $link = $i === $options['current'] ? '#' : $options['base'] . DIRECTORY_SEPARATOR . 'page' . DIRECTORY_SEPARATOR . $i . DIRECTORY_SEPARATOR; $replacements = [$class, $link, $i]; $pagination .= preg_replace($patterns, $replacements, $options['format']); $replacements = []; } # Show the back ellipsis $pagination .= $backEllipsis; # Show the last page $class = $options['current'] === $options['total'] ? 'active' : ''; $link = $options['current'] === $options['total'] ? '#' : $options['base'] . DIRECTORY_SEPARATOR . 'page' . DIRECTORY_SEPARATOR . $options['total'] . DIRECTORY_SEPARATOR; $replacements = [$class, $link, $options['total']]; $pagination .= preg_replace($patterns, $replacements, $options['format']); $replacements = []; # If show next if ($options['prev_next'] === true) { $class = $options['current'] < $options['total'] ? '' : 'disabled'; $link = $options['current'] < $options['total'] ? $options['base'] . DIRECTORY_SEPARATOR . 'page' . DIRECTORY_SEPARATOR . ($options['current'] + 1) . DIRECTORY_SEPARATOR : '#'; $replacements = [$class, $link, $options['next_text']]; $pagination .= preg_replace($patterns, $replacements, $options['format']); } return $pagination; }
/** * Get the count of all the articles for pagination total * * @return int */ private function countAllArticles() { if (!$this->isLoggedIn) { return false; } $perPage = 10; $offset = 0 * $perPage; $limit = $perPage; $articles = \Kanso\Kanso::getInstance()->Database->Builder()->SELECT('post.id')->FROM('posts')->FIND_ALL(); return count(\Kanso\Utility\Arr::paginate($articles, 0, 10)); }
public function save() { # Initialize joins if they have not been already $this->getTheCategory(); $this->getTheTags(); $this->getTheAuthor(); $this->getTheContent(); # Get the bookkeeper $bookkeeper = \Kanso\Kanso::getInstance()->Bookkeeper; # Update Kanso's static pages if the status has changed # and/or the article type has changed if ($this->row['status'] !== $this->tmpRow['status'] && isset($this->row['id'])) { $bookkeeper->changeStatus($this->row['id'], $this->tmpRow['status']); } # If no updates are required return; if ($this->tmpRow === $this->row && isset($this->row['id'])) { return; } # Save the article $save = $bookkeeper->saveArticle(array_merge($this->row, $this->tmpRow)); # Merge the results if ($save) { $save['tags'] = \Kanso\Utility\Arr::implodeByKey('name', $save['tags'], ', '); $this->tmpRow = array_merge($this->tmpRow, $save); $this->row = array_merge($this->row, $save); return true; } else { return false; } }
/** * Send comment emails to subscribers where needed * * @param array $articleRow Associative array of article data * @param array $newComment Associative array of comment data * @return bool */ private static function sendCommentEmails($articleRow, $newComment) { # Is this a reply comment $isReply = $newComment['type'] === 'reply'; # Get a new Query builder $Query = self::$Kanso->Database()->Builder(); # Get all the comments from the article into a multi-array $allComments = self::buildCommentTree(self::$Kanso->Query->get_comments((int) $articleRow['id'], false)); # Get all the emails that are subscibed to the entire article $allEmails = self::getAllCommentEmails($allComments); # Get all the admin email address $adminEmails = $Query->SELECT('email')->FROM('users')->WHERE('status', '=', 'confirmed')->AND_WHERE('role', '=', 'administrator')->AND_WHERE('email_notifications', '=', true)->FIND_ALL(); # Get all the emails that are subscribed to the thread $threadEmails = []; $parentComment = []; if ($isReply) { $threadEmails = self::getThreadEmails(self::getTopCommentThread($newComment, $allComments)); $parentComment = self::$Kanso->Query->get_comment($newComment['parent']); } # Build an array with comment variables to send email $website = self::$Kanso->Environment['KANSO_WEBSITE_NAME']; $commentVars = ['name' => $newComment['name'], 'id' => $newComment['id'], 'date' => $newComment['date'], 'articlePermalink' => self::$Kanso->Query->the_permalink($articleRow['id']), 'articleTitle' => $articleRow['title'], 'avatar' => self::$Kanso->Query->get_avatar($newComment['email'], 20, true), 'content' => self::cleanHTMLTags($newComment['html_content']), 'websiteLink' => self::$Kanso->Environment['HTTP_HOST'], 'website' => $website]; # If this is a reply we need the parent comment if ($isReply) { # Append the parent comment to the comment vars array $commentVars['parent'] = ['name' => $parentComment['name'], 'id' => $parentComment['id'], 'date' => $parentComment['date'], 'avatar' => self::$Kanso->Query->get_avatar($parentComment['email'], 20, true), 'content' => self::cleanHTMLTags($parentComment['html_content'])]; } $msg = $isReply ? \Kanso\Templates\Templater::getTemplate($commentVars, 'EmailReplyComment') : \Kanso\Templates\Templater::getTemplate($commentVars, 'EmailStandAloneComment'); # Send emails to thread subscribers if ($isReply && !empty($threadEmails)) { foreach ($threadEmails as $emailAddress => $name) { # Don't send emails to the peson commenting if ($emailAddress === $newComment['email']) { continue; } # Don't send emails to admins if (\Kanso\Utility\Arr::inMulti($emailAddress, $adminEmails)) { continue; } # Send the email \Kanso\Utility\Mailer::sendHTMLEmail($emailAddress, $website, 'no-reply@' . $website, 'Someone just replied to a comment at you made at ' . $website . ' on ' . $articleRow['title'] . '.', $msg); } } # Send email to all subscribers if (!empty($allEmails)) { foreach ($allEmails as $emailAddress => $name) { # Don't send emails to the peson commenting if ($emailAddress === $newComment['email']) { continue; } # Don't send email twice to people who have subscribed to their own comment # as well as the entire article if (isset($threadEmails[$emailAddress])) { continue; } # Don't send emails to admins if (\Kanso\Utility\Arr::inMulti($emailAddress, $adminEmails)) { continue; } \Kanso\Utility\Mailer::sendHTMLEmail($emailAddress, $website, 'no-reply@' . $website, 'A new comment was made at ' . $website . ' on ' . $articleRow['title'], $msg); } } # Send the email to all the admins on the Kanso blog $admins = $Query->SELECT('*')->FROM('users')->WHERE('status', '=', 'confirmed')->AND_WHERE('role', '=', 'administrator')->FIND_ALL(); foreach ($admins as $admin) { # Don't send emails to the peson commenting if ($admin['email'] === $newComment['email']) { continue; } # Add the admin to the comment variables $commentVars['admin'] = $admin; # Reset the email message $msg = \Kanso\Templates\Templater::getTemplate($commentVars, 'EmailAdminComment'); # Send the email \Kanso\Utility\Mailer::sendHTMLEmail($admin['email'], $website, 'no-reply@' . $website, 'A new comment was made at ' . $website . ' on ' . $articleRow['title'], $msg); } }
/** * Get the comments for listing in the admin panel * * @param $queries array POST data from client * @param $filter string * @return array */ public function loadAllComments($queries, $filter) { # Get the Kanso Query object $Query = \Kanso\Kanso::getInstance()->Query(); # Get the SQL builder $SQL = \Kanso\Kanso::getInstance()->Database->Builder(); $isSearch = $queries['search'] !== 'false'; $page = (int) $queries['page'] - 1; $comments = []; $sort = $queries['sortBy'] === 'newest' ? 'DESC' : 'ASC'; if ($isSearch) { $validKeys = ['ip' => 'ip_address', 'status' => 'status', 'user' => 'name', 'email' => 'email']; $searchValue = $queries['search']; $searchKey = false; if (\Kanso\Utility\Str::contains($searchValue, ':')) { $value = \Kanso\Utility\Str::getAfterFirstChar($searchValue, ':'); $key = \Kanso\Utility\Str::getBeforeFirstChar($searchValue, ':'); $key = isset($validKeys[$key]) ? $validKeys[$key] : false; if ($key) { $searchKey = $key; $searchValue = $value; } } if ($searchKey) { $comments = $SQL->SELECT('*')->FROM('comments')->WHERE($searchKey, '=', $searchValue); } else { $comments = $SQL->SELECT('*')->FROM('comments')->WHERE('content', 'LIKE', "%{$searchValue}%"); } if ($filter === 'all') { $comments = $comments->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'approved') { $comments = $comments->WHERE('status', '=', 'approved')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'spam') { $comments = $comments->WHERE('status', '=', 'spam')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'pending') { $comments = $comments->WHERE('status', '=', 'pending')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'deleted') { $comments = $comments->WHERE('status', '=', 'deleted')->ORDER_BY('date', $sort)->FIND_ALL(); } } else { if ($filter === 'all') { $comments = $SQL->SELECT('*')->FROM('comments')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'approved') { $comments = $SQL->SELECT('*')->FROM('comments')->WHERE('status', '=', 'approved')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'spam') { $comments = $SQL->SELECT('*')->FROM('comments')->WHERE('status', '=', 'spam')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'pending') { $comments = $SQL->SELECT('*')->FROM('comments')->WHERE('status', '=', 'pending')->ORDER_BY('date', $sort)->FIND_ALL(); } if ($filter === 'deleted') { $comments = $SQL->SELECT('*')->FROM('comments')->WHERE('status', '=', 'deleted')->ORDER_BY('date', $sort)->FIND_ALL(); } } foreach ($comments as $key => $comment) { $comments[$key]['permalink'] = $Query->the_permalink($comment['post_id']); $comments[$key]['title'] = $Query->the_title($comment['post_id']); $comments[$key]['avatar'] = $Query->get_avatar($comment['email'], 100, true); } $comments = \Kanso\Utility\Arr::paginate($comments, $page, 10); return $comments; }