From 37793064db51aaf08bb5f2904fb347b5a5e04248 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 09:57:42 -0500 Subject: [PATCH 01/10] ! refactor MessageIndex, add quick topic capacity --- sources/ElkArte/Controller/MessageIndex.php | 979 +++++++++++++------- themes/default/MessageIndex.template.php | 123 ++- 2 files changed, 781 insertions(+), 321 deletions(-) diff --git a/sources/ElkArte/Controller/MessageIndex.php b/sources/ElkArte/Controller/MessageIndex.php index 047c68e9ed..c92c1c6f7d 100644 --- a/sources/ElkArte/Controller/MessageIndex.php +++ b/sources/ElkArte/Controller/MessageIndex.php @@ -23,6 +23,8 @@ use ElkArte\EventManager; use ElkArte\FrontpageInterface; use ElkArte\Helper\DataValidator; +use ElkArte\MembersList; +use ElkArte\Themes\TemplateLayers; use ElkArte\TopicUtil; use ElkArte\User; @@ -31,6 +33,27 @@ */ class MessageIndex extends AbstractController implements FrontpageInterface { + /** @var string The db column wer are going to sort */ + public $sort_column = ''; + + /** @var array Know sort methods to db column */ + public $sort_methods = []; + + /** @var bool Sort direction asc or desc */ + public $ascending = ''; + + /** @var TemplateLayers The template layers object */ + private $template_layers; + + /** @var bool if we are marking as read */ + public $is_marked_notify; + + /** @var string Chosen sort method from the request */ + public $sort_by; + + /** @var int Basically the page start */ + public $sort_start; + /** * {@inheritDoc} */ @@ -69,7 +92,7 @@ public static function frontPageOptions() }); // Trigger change event - var event = new Event("change"); + let event = new Event("change"); document.getElementById("front_page").dispatchEvent(event);', true); return [['select', 'message_index_frontpage', self::_getBoardsList()]]; @@ -132,149 +155,411 @@ public function action_index() */ public function action_messageindex() { - global $txt, $board, $modSettings, $context, $options, $settings, $board_info; - - // Fairly often, we'll work with boards. Current board, sub-boards. - require_once(SUBSDIR . '/Boards.subs.php'); - require_once(SUBSDIR . '/MessageIndex.subs.php'); + global $txt, $context, $board_info; - // If this is a redirection board head off. + // Check for redirection board, and if found, head off if ($board_info['redirect']) { - incrementBoard($board, 'num_posts'); - redirectexit($board_info['redirect']); + $this->handleRedirectBoard(); } - theme()->getTemplates()->load('MessageIndex'); - loadJavascriptFile('topic.js'); + // Load any necessary resources + $this->loadSupportingResources(); - $context['name'] = $board_info['name']; - $context['sub_template'] = 'topic_listing'; - $context['description'] = ParserWrapper::instance()->parseBoard($board_info['description']); - $template_layers = theme()->getLayers(); + // Initialize $context + $this->initializeContext(); - // How many topics do we have in total? - $board_info['total_topics'] = allowedTo('approve_posts') ? $board_info['num_topics'] + $board_info['unapproved_topics'] : $board_info['num_topics'] + $board_info['unapproved_user_topics']; + // Build a list of unapproved posts, if applicable + if ($this->currentUserCanApprovePosts() && $this->hasUnapprovedPosts()) + { + $context['unapproved_posts_message'] = $this->buildUnapprovedPostsMessage(); + } - // View all the topics, or just a few? - $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics']; - $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; - $maxindex = isset($this->_req->query->all) && !empty($modSettings['enableAllMessages']) ? $board_info['total_topics'] : $context['topics_per_page']; + // Make sure the starting place makes sense and construct the page index + $this->setPageNavigation(); + + // Prepare profile links to those who can moderate on this board + $this->setBoardModeratorLinks(); - // Right, let's only index normal stuff! - $context['robot_no_index'] = $this->setRobotNoIndex(); + // Mark current and parent boards as seen. + $this->markCurrentAndParentBoardsAsSeen(); + + // Load basic information about the boards children, aka sub boards + $this->prepareSubBoardsForDisplay(); + + // Who else is taking a look + $this->prepareWhoViewing(); + + // Setup topic sort icons/options for template use + $this->setSortIcons(); + + // Load the topic listing, accounting for sort, start page, etc. + $this->loadBoardTopics(); + + // What quick moderation options are available? + $this->quickModeration(); - // If we can view unapproved messages and there are some build up a list. - if (allowedTo('approve_posts') && ($board_info['unapproved_topics'] || $board_info['unapproved_posts'])) + // Set template details/layers + $this->template_layers = theme()->getLayers(); + if (!empty($context['boards']) && $this->sort_start === 0) { - $untopics = $board_info['unapproved_topics'] ? '' . $board_info['unapproved_topics'] . '' : 0; - $unposts = $board_info['unapproved_posts'] ? '' . ($board_info['unapproved_posts'] - $board_info['unapproved_topics']) . '' : 0; - $context['unapproved_posts_message'] = sprintf($txt['there_are_unapproved_topics'], $untopics, $unposts, getUrl('action', ['action' => 'moderate', 'area' => 'postmod', 'sa' => ($board_info['unapproved_topics'] ? 'topics' : 'posts'), 'brd' => $board])); + $this->template_layers->add('display_child_boards'); } - // Known sort methods. - $sort_methods = messageIndexSort(); - $default_sort_method = 'last_post'; - $sort_string = ''; + // If there are children, but no topics and no ability to post topics... + $context['no_topic_listing'] = !empty($context['boards']) && empty($context['topics']) && !$context['can_post_new']; + + $this->template_layers->add('topic_listing'); - // Requested a sorting method - $chosen_sort = $this->_req->getQuery('sort', 'trim'); - if (isset($chosen_sort)) + theme()->addJavascriptVar(['notification_board_notice' => $this->is_marked_notify ? $txt['notification_disable_board'] : $txt['notification_enable_board']], true); + + // Is Quick Topic available + $this->quickTopic(); + + // Finally action buttons like start new topic, notify, mark read ... + $this->buildBoardButtons(); + } + + /** + * Handles redirection for a board. Increments the number of posts in the board + * and redirects to the specified board URL. + */ + private function handleRedirectBoard(): void + { + global $board, $board_info; + + // If this is a redirection board head off. + require_once(SUBSDIR . '/Boards.subs.php'); + + incrementBoard($board, 'num_posts'); + redirectexit($board_info['redirect']); + } + + /** + * Initializes the context by setting various variables for the template. + */ + private function initializeContext(): void + { + global $txt, $context, $board_info, $modSettings; + + $description = ParserWrapper::instance()->parseBoard($board_info['description']); + + $context += [ + 'name' => $board_info['name'], + 'sub_template' => 'topic_listing', + 'description' => $description, + 'robot_no_index' => $this->setRobotNoIndex(), + // 'Print' the header and board info. + 'page_title' => strip_tags($board_info['name']), + 'page_description' => strip_tags($description), + // Set the variables up for the template. + 'can_mark_notify' => $this->currentUserCanMarkNotify(), + 'can_post_new' => $this->currentUserCanPostNew(), + 'can_post_poll' => $this->currentUserCanPostPoll(), + 'can_moderate_forum' => $this->currentUserCanModerate(), + 'can_approve_posts' => $this->currentUserCanApprovePosts(), + 'jump_to' => [ + 'label' => addslashes(un_htmlspecialchars($txt['jump_to'])), + 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), ['&' => '&']), ENT_COMPAT, 'UTF-8'), + 'child_level' => $board_info['child_level'], + ], + 'message_index_preview' => !empty($modSettings['message_index_preview']) + ]; + } + + /** + * Sets if this is a page that we do, or do not, want bots to index + * + * @return bool + */ + public function setRobotNoIndex() + { + global $context; + + foreach ($this->_req->query as $k => $v) { - // We only know these. - if (!isset($sort_methods[$chosen_sort])) + // Don't index a sort result etc. + if (!in_array($k, ['board', 'start', session_name()], true)) { - $chosen_sort = $default_sort_method; + return true; } + } + + return !empty($this->_req->query->start) + && (!is_numeric($this->_req->query->start) || $this->_req->query->start % $context['messages_per_page'] !== 0); + } + + /** + * Checks whether the current user has permission to mark notifications + * + * @return bool True if the current user can mark notifications, false otherwise + */ + private function currentUserCanMarkNotify(): bool + { + return allowedTo('mark_notify') && $this->user->is_guest === false; + } + + /** + * Checks if the current user is allowed to post new topics + * + * @return bool Returns true if the current user is allowed to post new topics, otherwise false. + */ + private function currentUserCanPostNew(): bool + { + global $modSettings; + + return allowedTo('post_new') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_topics')); + } - $sort_string = ';sort=' . $chosen_sort . (isset($this->_req->query->desc) ? ';desc' : ''); + /** + * Checks if the current user can post a poll + * + * @return bool Returns true if the current user can post a poll, false otherwise + */ + private function currentUserCanPostPoll(): bool + { + global $modSettings; + + return !empty($modSettings['pollMode']) && allowedTo('poll_post') && $this->currentUserCanPostNew(); + } + + /** + * Checks if the current user is allowed to moderate the forum + * + * @return bool Returns true if the current user is allowed to moderate the forum, false otherwise + */ + private function currentUserCanModerate(): bool + { + return allowedTo('moderate_forum'); + } + + /** + * Checks if the current user has the permission to approve posts + * + * @return bool True if the current user can approve posts, false otherwise + */ + private function currentUserCanApprovePosts(): bool + { + return allowedTo('approve_posts'); + } + + /** + * Check if the current user can restore a topic + * + * @return bool True if they can restore a topic + */ + private function currentUserCanRestore(): bool + { + global $modSettings, $board; + + return allowedTo('move_any') && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board; + } + + /** + * Loads supporting resources for the MessageIndex page. + */ + private function loadSupportingResources(): void + { + global $modSettings, $txt; + + // Fairly often, we'll work with boards. Current board, sub-boards. + require_once(SUBSDIR . '/Boards.subs.php'); + require_once(SUBSDIR . '/MessageIndex.subs.php'); + + theme()->getTemplates()->load('MessageIndex'); + loadJavascriptFile('topic.js'); + + if (!empty($modSettings['message_index_preview'])) + { + loadJavascriptFile('elk_toolTips.js', ['defer' => true]); } + theme()->addJavascriptVar([ + 'txt_mark_as_read_confirm' => $txt['mark_these_as_read_confirm'] + ], true); + } + + /** + * Checks if the current board has unapproved posts or topics. + * + * @return bool Returns true if the board has unapproved posts or topics, otherwise false. + */ + private function hasUnapprovedPosts(): bool + { + global $board_info; + + return $board_info['unapproved_topics'] || $board_info['unapproved_posts']; + } + + /** + * Builds the message/links for the number of unapproved posts and topics in the current board. + * + * @return string The message containing the number of unapproved topics and posts. + */ + private function buildUnapprovedPostsMessage(): string + { + global $txt, $board_info, $board; + + $unApprovedTopics = $board_info['unapproved_topics'] ? '' . $board_info['unapproved_topics'] . '' : 0; + $unApprovedPosts = $board_info['unapproved_posts'] ? '' . ($board_info['unapproved_posts'] - $board_info['unapproved_topics']) . '' : 0; + + return sprintf($txt['there_are_unapproved_topics'], $unApprovedTopics, $unApprovedPosts, getUrl('action', ['action' => 'moderate', 'area' => 'postmod', 'sa' => ($board_info['unapproved_topics'] ? 'topics' : 'posts'), 'brd' => $board])); + } + + /** + * Sets up the page navigation for the board view. + */ + private function setPageNavigation() + { + global $board, $modSettings, $context, $options, $board_info; + + // How many topics do we have in total? + $board_info['total_topics'] = $this->currentUserCanApprovePosts() + ? $board_info['num_topics'] + $board_info['unapproved_topics'] + : $board_info['num_topics'] + $board_info['unapproved_user_topics']; + + $all = $this->_req->isSet('all'); + $start = $this->_req->getQuery('start', 'intval', 0); + + // View all the topics, or just a few? + $context['topics_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['topics_per_page']) ? $options['topics_per_page'] : $modSettings['defaultMaxTopics']; + $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) ? $options['messages_per_page'] : $modSettings['defaultMaxMessages']; + $per_page = $all && !empty($modSettings['enableAllMessages']) ? $board_info['total_topics'] : $context['topics_per_page']; + // Make sure the starting place makes sense and construct the page index. - $context['page_index'] = constructPageIndex('{scripturl}?board=' . $board . '.%1$d' . $sort_string, $this->_req->query->start, $board_info['total_topics'], $maxindex, true); - $context['start'] = &$this->_req->query->start; + $context['page_index'] = constructPageIndex('{scripturl}?board=' . $board . '.%1$d' . $this->buildSortingString(), $start, $board_info['total_topics'], $per_page, true); // Set a canonical URL for this page. - $context['canonical_url'] = getUrl('board', ['board' => $board, 'start' => $context['start'], 'name' => $board_info['name']]); + $context['canonical_url'] = getUrl('board', ['board' => $board, 'start' => $start, 'name' => $board_info['name']]); $context['links'] += [ - 'prev' => $this->_req->query->start >= $context['topics_per_page'] ? getUrl('board', ['board' => $board, 'start' => $this->_req->query->start - $context['topics_per_page'], 'name' => $board_info['name']]) : '', - 'next' => $this->_req->query->start + $context['topics_per_page'] < $board_info['total_topics'] ? getUrl('board', ['board' => $board, 'start' => $this->_req->query->start + $context['topics_per_page'], 'name' => $board_info['name']]) : '', + 'prev' => $start >= $context['topics_per_page'] ? getUrl('board', ['board' => $board, 'start' => $start - $context['topics_per_page'], 'name' => $board_info['name']]) : '', + 'next' => $start + $context['topics_per_page'] < $board_info['total_topics'] ? getUrl('board', ['board' => $board, 'start' => $start + $context['topics_per_page'], 'name' => $board_info['name']]) : '', ]; - if (isset($this->_req->query->all) && !empty($modSettings['enableAllMessages']) && $maxindex > $modSettings['enableAllMessages']) + if ($all && !empty($modSettings['enableAllMessages']) && $per_page > $modSettings['enableAllMessages']) + { + $per_page = $modSettings['enableAllMessages']; + $start = 0; + } + + $this->sort_start = $start; + $context['start'] = $start; + $context['per_page'] = $per_page; + } + + /** + * Builds the sorting string for the message index page. + * + * @return string The sorting string with the chosen sort method and direction + */ + private function buildSortingString() + { + global $context, $txt; + + // Known sort methods. + $this->sort_methods = messageIndexSort(); + $default_sort_method = 'last_post'; + + // Requested a sorting method? + $chosen_sort = $this->_req->getQuery('sort', 'trim', $default_sort_method); + + // We only know these. + if (!isset($this->sort_methods[$chosen_sort])) { - $maxindex = $modSettings['enableAllMessages']; - $this->_req->query->start = 0; + $chosen_sort = $default_sort_method; } + $sort_string = ';sort=' . $chosen_sort . ($this->_req->isSet('desc') ? ';desc' : ''); + $this->sort_by = $chosen_sort; + $this->ascending = $this->_req->isSet('asc'); + $this->sort_column = $this->sort_methods[$this->sort_by]; + + $context['sort_by'] = $this->sort_by; + $context['sort_direction'] = $this->ascending ? 'up' : 'down'; + $context['sort_title'] = $this->ascending ? $txt['sort_desc'] : $txt['sort_asc']; + + return $sort_string; + } + + /** + * Loads board moderator links into the context for displaying on the template. + */ + private function setBoardModeratorLinks() + { + global $board_info, $context, $txt; + // Build a list of the board's moderators. $context['moderators'] = &$board_info['moderators']; $context['link_moderators'] = []; + if (!empty($board_info['moderators'])) { foreach ($board_info['moderators'] as $mod) { - $context['link_moderators'][] = '' . $mod['name'] . ''; + $context['link_moderators'][] = '' . $mod['name'] . ''; } } + } - // Mark current and parent boards as seen. - if ($this->user->is_guest === false) + /** + * Marks the current board and its parent boards as seen for the current user + */ + public function markCurrentAndParentBoardsAsSeen() + { + global $board_info, $board; + + if ($this->user->is_guest) { - // We can't know they read it if we allow prefetches. - stop_prefetching(); + $this->is_marked_notify = false; + return; + } - // Mark the board as read, and its parents. - if (!empty($board_info['parent_boards'])) - { - $board_list = array_keys($board_info['parent_boards']); - $board_list[] = $board; - } - else - { - $board_list = [$board]; - } + // We can't know they read it if we allow prefetches. + stop_prefetching(); + + // Mark the board as read, and its parents. + if (!empty($board_info['parent_boards'])) + { + $board_list = array_keys($board_info['parent_boards']); + $board_list[] = $board; + } + else + { + $board_list = [$board]; + } - // Mark boards as read. Boards alone, no need for topics. - markBoardsRead($board_list, false, false); + // Mark boards as read. Boards alone, no need for topics. + markBoardsRead($board_list); - // Clear topicseen cache - if (!empty($board_info['parent_boards'])) + // Clear topicseen cache + if (!empty($board_info['parent_boards'])) + { + // We've seen all these boards now! + foreach ($board_info['parent_boards'] as $k => $dummy) { - // We've seen all these boards now! - foreach ($board_info['parent_boards'] as $k => $dummy) + if (isset($_SESSION['topicseen_cache'][$k])) { - if (isset($_SESSION['topicseen_cache'][$k])) - { - unset($_SESSION['topicseen_cache'][$k]); - } + unset($_SESSION['topicseen_cache'][$k]); } } - - if (isset($_SESSION['topicseen_cache'][$board])) - { - unset($_SESSION['topicseen_cache'][$board]); - } - - // From now on, they've seen it. So we reset notifications. - $context['is_marked_notify'] = resetSentBoardNotification($this->user->id, $board); } - else + + if (isset($_SESSION['topicseen_cache'][$board])) { - $context['is_marked_notify'] = false; + unset($_SESSION['topicseen_cache'][$board]); } - // 'Print' the header and board info. - $context['page_title'] = strip_tags($board_info['name']); - $context['page_description'] = strip_tags($context['description']); + // From now on, they've seen it. So we reset notifications. + $this->is_marked_notify = resetSentBoardNotification($this->user->id, $board); + } - // Set the variables up for the template. - $context['can_mark_notify'] = allowedTo('mark_notify') && $this->user->is_guest === false; - $context['can_post_new'] = allowedTo('post_new') || ($modSettings['postmod_active'] && allowedTo('post_unapproved_topics')); - $context['can_post_poll'] = !empty($modSettings['pollMode']) && allowedTo('poll_post') && $context['can_post_new']; - $context['can_moderate_forum'] = allowedTo('moderate_forum'); - $context['can_approve_posts'] = allowedTo('approve_posts'); + /** + * Prepare and load sub-boards for display. + */ + private function prepareSubBoardsForDisplay() + { + global $board_info, $modSettings, $context; // Prepare sub-boards for display. $boardIndexOptions = [ @@ -284,8 +569,17 @@ public function action_messageindex() 'set_latest_post' => false, 'countChildPosts' => !empty($modSettings['countChildPosts']), ]; - $boardlist = new BoardsList($boardIndexOptions); - $context['boards'] = $boardlist->getBoards(); + + $boardList = new BoardsList($boardIndexOptions); + $context['boards'] = $boardList->getBoards(); + } + + /** + * Prepares and loads into context the information about who is currently viewing the board + */ + private function prepareWhoViewing() + { + global $settings, $board; // Nosey, nosey - who's viewing this board? if (!empty($settings['display_who_viewing'])) @@ -293,41 +587,51 @@ public function action_messageindex() require_once(SUBSDIR . '/Who.subs.php'); formatViewers($board, 'board'); } + } - // Set the sort - $context['sort_by'] = $chosen_sort ?? $default_sort_method; - $ascending = isset($this->_req->query->asc); - $sort_column = $sort_methods[$context['sort_by']]; - $context['sort_direction'] = $ascending ? 'up' : 'down'; - $context['sort_title'] = $ascending ? $txt['sort_desc'] : $txt['sort_asc']; + /** + * Sets the sort icons for the topics headers in the context. + */ + private function setSortIcons() + { + global $context, $board, $board_info, $txt; // Trick $txt['starter'] = $txt['started_by']; // todo: Need to move this to theme. - foreach ($sort_methods as $key => $val) + foreach ($this->sort_methods as $key => $val) { - $sorticon = match ($key) + $sortIcon = match ($key) { 'subject', 'starter', 'last_poster' => 'alpha', default => 'numeric', }; $context['topics_headers'][$key] = [ - 'url' => getUrl('board', ['board' => $context['current_board'], 'start' => $context['start'], 'sort' => $key, 'name' => $board_info['name'], $context['sort_by'] == $key && $context['sort_direction'] === 'up' ? 'desc' : 'asc']), - 'sort_dir_img' => $context['sort_by'] === $key ? '' . $context['sort_title'] . '' : '', + 'url' => getUrl('board', ['board' => $board, 'start' => $this->sort_start, 'sort' => $key, 'name' => $board_info['name'], $this->sort_by === $key && $this->ascending ? 'desc' : 'asc']), + 'sort_dir_img' => $this->sort_by === $key ? '' . $context['sort_title'] . '' : '', ]; } + } + + /** + * Loads board topics into the context + */ + private function loadBoardTopics() + { + global $board, $modSettings, $context, $settings, $board_info; // Calculate the fastest way to get the topics. $start = $this->_req->getQuery('start', 'intval', 0); + $per_page = $context['per_page']; $fake_ascending = false; if ($start > ($board_info['total_topics'] - 1) / 2) { - $ascending = !$ascending; + $this->ascending = !$this->ascending; $fake_ascending = true; - $maxindex = $board_info['total_topics'] < $start + $maxindex + 1 ? $board_info['total_topics'] - $start : $maxindex; - $start = $board_info['total_topics'] < $start + $maxindex + 1 ? 0 : $board_info['total_topics'] - $start - $maxindex; + $per_page = $board_info['total_topics'] < $start + $per_page + 1 ? $board_info['total_topics'] - $start : $per_page; + $start = $board_info['total_topics'] < $start + $per_page + 1 ? 0 : $board_info['total_topics'] - $start - $per_page; } $context['topics'] = []; @@ -337,14 +641,14 @@ public function action_messageindex() 'only_approved' => $modSettings['postmod_active'] && !allowedTo('approve_posts'), 'previews' => empty($modSettings['message_index_preview']) ? 0 : (empty($modSettings['preview_characters']) ? -1 : $modSettings['preview_characters']), 'include_avatars' => $settings['avatars_on_indexes'], - 'ascending' => $ascending, + 'ascending' => $this->ascending, 'fake_ascending' => $fake_ascending ]; // Allow integration to modify / add to the $indexOptions - call_integration_hook('integrate_messageindex_topics', [&$sort_column, &$indexOptions]); + call_integration_hook('integrate_messageindex_topics', [&$this->sort_column, &$indexOptions]); - $topics_info = messageIndexTopics($board, $this->user->id, $start, $maxindex, $context['sort_by'], $sort_column, $indexOptions); + $topics_info = messageIndexTopics($board, $this->user->id, $start, $per_page, $this->sort_by, $this->sort_column, $indexOptions); $context['topics'] = TopicUtil::prepareContext($topics_info, false, empty($modSettings['preview_characters']) ? 128 : $modSettings['preview_characters']); @@ -371,28 +675,33 @@ public function action_messageindex() // Trigger a topic loaded event $this->_events->trigger('topicinfo', ['callbacks' => &$context['topics']]); + } - $context['jump_to'] = [ - 'label' => addslashes(un_htmlspecialchars($txt['jump_to'])), - 'board_name' => htmlspecialchars(strtr(strip_tags($board_info['name']), ['&' => '&']), ENT_COMPAT, 'UTF-8'), - 'child_level' => $board_info['child_level'], - ]; + /** + * Determines which quick moderation actions are available for this user. + * Loads which actions are available, on a per-topic basis, into $context. + */ + private function quickModeration() + { + global $modSettings, $context, $options, $board_info; // Is Quick Moderation active/needed? if (!empty($options['display_quick_mod']) && !empty($context['topics'])) { - $context['can_markread'] = $context['user']['is_logged']; - $context['can_lock'] = allowedTo('lock_any'); - $context['can_sticky'] = allowedTo('make_sticky'); - $context['can_move'] = allowedTo('move_any'); - $context['can_remove'] = allowedTo('remove_any'); - $context['can_merge'] = allowedTo('merge_any'); + $context += [ + 'can_markread' => $context['user']['is_logged'], + 'can_lock' => allowedTo('lock_any'), + 'can_sticky' => allowedTo('make_sticky'), + 'can_move' => allowedTo('move_any'), + 'can_remove' => allowedTo('remove_any'), + 'can_merge' => allowedTo('merge_any'), + ]; // Ignore approving own topics as it's unlikely to come up... $context['can_approve'] = $modSettings['postmod_active'] && allowedTo('approve_posts') && !empty($board_info['unapproved_topics']); // Can we restore topics? - $context['can_restore'] = allowedTo('move_any') && !empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] == $board; + $context['can_restore'] = $this->currentUserCanRestore(); // Set permissions for all the topics. foreach ($context['topics'] as $t => $topic) @@ -411,26 +720,195 @@ public function action_messageindex() $context['can_remove'] |= ($started && allowedTo('remove_own')); } - // Can we even use quick moderation on this batch? - $context['can_quick_mod'] = $context['user']['is_logged'] || $context['can_approve'] || $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'] || $context['can_merge'] || $context['can_restore']; - if (!empty($context['can_quick_mod'])) + // Can we even use quick moderation on this batch? + $context['can_quick_mod'] = $context['user']['is_logged'] || $context['can_approve'] || $context['can_remove'] || $context['can_lock'] || $context['can_sticky'] || $context['can_move'] || $context['can_merge'] || $context['can_restore']; + if (!empty($context['can_quick_mod'])) + { + $this->buildQuickModerationButtons(); + $context['qmod_actions'] = ['approve', 'remove', 'lock', 'sticky', 'move', 'merge', 'restore', 'markread']; + call_integration_hook('integrate_quick_mod_actions'); + } + } + } + + /** + * Loads into $context the moderation button array for template use. + * Call integrate_message_index_mod_buttons hook + */ + public function buildQuickModerationButtons() + { + global $context; + + $context['can_show'] = false; + $quickMod = array_column($context['topics'], 'quick_mod', 'id'); + $context['show_qm_message_checkbox'] = array_column($context['topics'], 'id'); + + // Build valid topic id's by action + $keys = array_keys($quickMod); + foreach (['move', 'lock', 'remove', 'approve'] as $area) + { + // e.g. get topic id's where this quick_mod action xxx value is valid + $temp = array_combine($keys, array_column($quickMod, $area)); + $context['allow_qm']['can_' . $area] = array_keys($temp, true); + ${'show_' . $area} = !empty($context['allow_qm']['can_' . $area]); + } + + // Build the mod button array with buttons that are valid for, at least some, of the messages + $context['mod_buttons'] = [ + 'move' => [ + 'test' => $show_move ? 'can_move' : 'can_show', + 'text' => 'move_topic', + 'id' => 'move', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + 'remove' => [ + 'test' => $show_remove ? 'can_remove' : 'can_show', + 'text' => 'remove_topic', + 'id' => 'remove', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + 'lock' => [ + 'test' => $show_lock ? 'can_lock' : 'can_show', + 'text' => 'set_lock', + 'id' => 'lock', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + 'approve' => [ + 'test' => $show_approve ? 'can_approve' : 'can_show', + 'text' => 'approve', + 'id' => 'approve', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + 'sticky' => [ + 'test' => 'can_sticky', + 'text' => 'set_sticky', + 'id' => 'sticky', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + 'merge' => [ + 'test' => 'can_merge', + 'text' => 'merge', + 'id' => 'merge', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + 'markread' => [ + 'test' => 'can_markread', + 'text' => 'mark_read_short', + 'id' => 'markread', + 'lang' => true, + 'url' => 'javascript:void(0);', + ], + ]; + + // Restore a topic, maybe even some doxing ! + if ($context['can_restore']) + { + $context['mod_buttons']['restore'] = [ + 'text' => 'restore_topic', + 'lang' => true, + 'url' => 'javascript:void(0);', + ]; + } + + // Allow adding new buttons easily. + call_integration_hook('integrate_message_index_quickmod_buttons'); + + $context['mod_buttons'] = array_reverse($context['mod_buttons']); + } + + /** + * Similar to Quick Reply, this is Quick Topic. + * Allows a way to start a new topic from the boards message index. + */ + private function quickTopic(): void + { + global $txt, $modSettings, $context, $options; + + // Quick topic enabled? + if ($context['can_post_new'] && !empty($options['display_quick_reply'])) + { + $this->prepareQuickTopic(); + + checkSubmitOnce('register'); + + $context['becomes_approved'] = true; + if ($modSettings['postmod_active'] && !allowedTo('post_new') && allowedTo('post_unapproved_topics')) { - $this->buildQuickModerationButtons(); - $context['qmod_actions'] = ['approve', 'remove', 'lock', 'sticky', 'move', 'merge', 'restore', 'markread']; - call_integration_hook('integrate_quick_mod_actions'); + $context['becomes_approved'] = false; + } + else + { + isAllowedTo('post_new'); } + + require_once(SUBSDIR . '/Editor.subs.php'); + // Create the editor for the QT area + $editorOptions = [ + 'id' => 'message', + 'value' => '', + 'labels' => [ + 'post_button' => $txt['post'], + ], + 'height' => '200px', + 'width' => '100%', + 'smiley_container' => 'smileyBox_message', + 'bbc_container' => 'bbcBox_message', + // We submit/switch to full post page for the preview + 'preview_type' => 1, + 'buttons' => [ + 'more' => [ + 'type' => 'submit', + 'name' => 'more_options', + 'value' => $txt['post_options'], + 'options' => '' + ] + ], + ]; + + // Trigger the prepare_context event for modules that have tied in to it + $this->_events->trigger('prepare_context', ['editorOptions' => &$editorOptions, 'use_quick_reply' => !empty($options['display_quick_reply'])]); + + create_control_richedit($editorOptions); + + theme()->getTemplates()->load('GenericMessages'); } + } + + /** + * If Quick Topic is on, we need to load user information into $context so the poster sidebar renders + */ + private function prepareQuickTopic(): void + { + global $options, $context; - if (!empty($context['boards']) && $context['start'] == 0) + if (empty($options['hide_poster_area']) && $options['display_quick_reply']) { - $template_layers->add('display_child_boards'); + MembersList::load(User::$info->id); + $thisUser = MembersList::get(User::$info->id); + $thisUser->loadContext(); + + $context['thisMember'] = [ + 'id' => 'new', + 'is_message_author' => true, + 'member' => $thisUser->toArray()['data'] + ]; + $context['can_issue_warning'] = allowedTo('issue_warning') && featureEnabled('w') && !empty($modSettings['warning_enable']); + $context['can_send_pm'] = allowedTo('pm_send'); } + } - // If there are children, but no topics and no ability to post topics... - $context['no_topic_listing'] = !empty($context['boards']) && empty($context['topics']) && !$context['can_post_new']; - $template_layers->add('topic_listing'); - - theme()->addJavascriptVar(['notification_board_notice' => $context['is_marked_notify'] ? $txt['notification_disable_board'] : $txt['notification_enable_board']], true); + /** + * Build the board buttons for the message index page. + */ + private function buildBoardButtons(): void + { + global $context, $settings, $board; // Build the message index button array. $context['normal_buttons'] = [ @@ -438,26 +916,22 @@ public function action_messageindex() 'test' => 'can_post_new', 'text' => 'new_topic', 'lang' => true, - 'url' => getUrl('action', ['action' => 'post', 'board' => $context['current_board'] . '.0']), + 'url' => getUrl('action', ['action' => 'post', 'board' => $board . '.0']), 'active' => true], 'notify' => [ 'test' => 'can_mark_notify', - 'text' => $context['is_marked_notify'] ? 'unnotify' : 'notify', + 'text' => $this->is_marked_notify ? 'unnotify' : 'notify', 'lang' => true, 'custom' => 'onclick="return notifyboardButton(this);"', - 'url' => getUrl('action', ['action' => 'notifyboard', 'sa' => ($context['is_marked_notify'] ? 'off' : 'on'), 'board' => $context['current_board'] . '.' . $context['start'], '{session_data}'])], + 'url' => getUrl('action', ['action' => 'notifyboard', 'sa' => ($this->is_marked_notify ? 'off' : 'on'), 'board' => $board . '.' . $this->sort_start, '{session_data}'])], ]; - theme()->addJavascriptVar([ - 'txt_mark_as_read_confirm' => $txt['mark_these_as_read_confirm'] - ], true); - // They can only mark read if they are logged in, and it's enabled! if ($this->user->is_guest === false && $settings['show_mark_read']) { $context['normal_buttons']['markread'] = [ 'text' => 'mark_read_short', 'lang' => true, - 'url' => getUrl('action', ['action' => 'markasread', 'sa' => 'board', 'board' => $context['current_board'] . '.0', '{session_data}']), + 'url' => getUrl('action', ['action' => 'markasread', 'sa' => 'board', 'board' => $board . '.0', '{session_data}']), 'custom' => 'onclick="return markboardreadButton(this);"' ]; } @@ -467,34 +941,6 @@ public function action_messageindex() // Trigger a post load event with quick access to normal buttons $this->_events->trigger('post_load', ['callbacks' => &$context['normal_buttons']]); - - if (!empty($modSettings['message_index_preview'])) - { - loadJavascriptFile('elk_toolTips.js', ['defer' => true]); - $context['message_index_preview'] = true; - } - } - - /** - * Sets if this is a page that we do, or do not, want bots to index - * - * @return bool - */ - public function setRobotNoIndex() - { - global $context; - - foreach ($this->_req->query as $k => $v) - { - // Don't index a sort result etc. - if (!in_array($k, ['board', 'start', session_name()], true)) - { - return true; - } - } - - return !empty($this->_req->query->start) - && (!is_numeric($this->_req->query->start) || $this->_req->query->start % $context['messages_per_page'] !== 0); } /** @@ -792,6 +1238,56 @@ public function action_quickmod() redirectexit($redirect_url); } + /** + * Just what actions can they perform on this board + * + * Checks if they can markread, sticky, move, remove, lock or merge + * + * @param array $boards_can + * @return array + */ + public function setPossibleQmActions($boards_can) + { + $possibleActions = []; + + if ($this->user->is_guest === false) + { + $possibleActions[] = 'markread'; + } + + if (!empty($boards_can['make_sticky'])) + { + $possibleActions[] = 'sticky'; + } + + if (!empty($boards_can['move_any']) || !empty($boards_can['move_own'])) + { + $possibleActions[] = 'move'; + } + + if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own'])) + { + $possibleActions[] = 'remove'; + } + + if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own'])) + { + $possibleActions[] = 'lock'; + } + + if (!empty($boards_can['merge_any'])) + { + $possibleActions[] = 'merge'; + } + + if (!empty($boards_can['approve_posts'])) + { + $possibleActions[] = 'approve'; + } + + return $possibleActions; + } + /** * Can they sticky a topic * @@ -851,145 +1347,4 @@ public function canLock($boards_can, $row) && $row['locked'] != 1 && (in_array(0, $boards_can['lock_own']) || in_array($row['id_board'], $boards_can['lock_own']))); } - - /** - * Just what actions can they perform on this board - * - * Checks if they can markread, sticky, move, remove, lock or merge - * - * @param array $boards_can - * @return array - */ - public function setPossibleQmActions($boards_can) - { - $possibleActions = []; - - if ($this->user->is_guest === false) - { - $possibleActions[] = 'markread'; - } - - if (!empty($boards_can['make_sticky'])) - { - $possibleActions[] = 'sticky'; - } - - if (!empty($boards_can['move_any']) || !empty($boards_can['move_own'])) - { - $possibleActions[] = 'move'; - } - - if (!empty($boards_can['remove_any']) || !empty($boards_can['remove_own'])) - { - $possibleActions[] = 'remove'; - } - - if (!empty($boards_can['lock_any']) || !empty($boards_can['lock_own'])) - { - $possibleActions[] = 'lock'; - } - - if (!empty($boards_can['merge_any'])) - { - $possibleActions[] = 'merge'; - } - - if (!empty($boards_can['approve_posts'])) - { - $possibleActions[] = 'approve'; - } - - return $possibleActions; - } - - /** - * Loads into $context the moderation button array for template use. - * Call integrate_message_index_mod_buttons hook - */ - public function buildQuickModerationButtons() - { - global $context; - - $context['can_show'] = false; - $quickMod = array_column($context['topics'], 'quick_mod', 'id'); - $context['show_qm_message_checkbox'] = array_column($context['topics'], 'id'); - - // Build valid topic id's by action - $keys = array_keys($quickMod); - foreach (['move', 'lock', 'remove', 'approve'] as $area) - { - // e.g. get topic id's where this quick_mod action xxx value is valid - $temp = array_combine($keys, array_column($quickMod, $area)); - $context['allow_qm']['can_' . $area] = array_keys($temp, true); - ${'show_' . $area} = !empty($context['allow_qm']['can_' . $area]); - } - - // Build the mod button array with buttons that are valid for, at least some, of the messages - $context['mod_buttons'] = [ - 'move' => [ - 'test' => $show_move ? 'can_move' : 'can_show', - 'text' => 'move_topic', - 'id' => 'move', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - 'remove' => [ - 'test' => $show_remove ? 'can_remove' : 'can_show', - 'text' => 'remove_topic', - 'id' => 'remove', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - 'lock' => [ - 'test' => $show_lock ? 'can_lock' : 'can_show', - 'text' => 'set_lock', - 'id' => 'lock', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - 'approve' => [ - 'test' => $show_approve ? 'can_approve' : 'can_show', - 'text' => 'approve', - 'id' => 'approve', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - 'sticky' => [ - 'test' => 'can_sticky', - 'text' => 'set_sticky', - 'id' => 'sticky', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - 'merge' => [ - 'test' => 'can_merge', - 'text' => 'merge', - 'id' => 'merge', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - 'markread' => [ - 'test' => 'can_markread', - 'text' => 'mark_read_short', - 'id' => 'markread', - 'lang' => true, - 'url' => 'javascript:void(0);', - ], - ]; - - // Restore a topic, maybe even some doxing ! - if ($context['can_restore']) - { - $context['mod_buttons']['restore'] = [ - 'text' => 'restore_topic', - 'lang' => true, - 'url' => 'javascript:void(0);', - ]; - } - - // Allow adding new buttons easily. - call_integration_hook('integrate_message_index_quickmod_buttons'); - - $context['mod_buttons'] = array_reverse($context['mod_buttons']); - } -} +} \ No newline at end of file diff --git a/themes/default/MessageIndex.template.php b/themes/default/MessageIndex.template.php index a55d681c9e..c033e1ede1 100644 --- a/themes/default/MessageIndex.template.php +++ b/themes/default/MessageIndex.template.php @@ -12,6 +12,8 @@ * */ +use ElkArte\MessageTopicIcons; + /** * Loads the template used to display boards */ @@ -73,7 +75,7 @@ function template_topic_listing_above() if (!empty($context['moderators'])) { echo ' -
', count($context['moderators']) === 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $context['link_moderators']), '.
'; +
', count($context['moderators']) === 1 ? $txt['moderator'] : $txt['moderators'], ': ', implode(', ', $context['link_moderators']), '.
'; } echo ' @@ -85,11 +87,11 @@ function template_topic_listing_above() { if ($settings['display_who_viewing'] == 1) { - echo count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members']; + echo '', count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members']; } else { - echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo '', empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); } echo $txt['who_and'], $context['view_num_guests'], ' ', $context['view_num_guests'] == 1 ? $txt['guest'] : $txt['guests'], $txt['who_viewing_board']; @@ -145,18 +147,21 @@ function template_topic_listing() if (!$context['no_topic_listing']) { - // If Quick Moderation is enabled start the form. - if (!empty($context['can_quick_mod']) && !empty($options['display_quick_mod']) && !empty($context['topics'])) + // If this person can approve items, and we have some awaiting approval tell them. + if (!empty($context['unapproved_posts_message'])) { echo ' -
'; +
', $context['unapproved_posts_message'], '
'; } - // If this person can approve items, and we have some awaiting approval tell them. - if (!empty($context['unapproved_posts_message'])) + // Quick Topic enabled ? + template_quicktopic_above(); + + // If Quick Moderation is enabled start the form. + if (!empty($context['can_quick_mod']) && !empty($options['display_quick_mod']) && !empty($context['topics'])) { echo ' -
', $context['unapproved_posts_message'], '
'; + '; } echo ' @@ -486,3 +491,103 @@ function template_topic_listing_below() ); } } + +/** + * This is quick topic area above the topic listing, shown when the subject input gains focus + */ +function template_quicktopic_above() +{ + global $context, $options, $txt, $modSettings, $settings; + + // Using quick topic, and you can start a new topic? + if ($context['can_post_new'] && !empty($options['display_quick_reply']) && !$context['user']['is_guest']) + { + echo ' + + +
'; + + quickTopicToggle(); + } +} + +/** + * Adds needed JS to show the quick topic area + */ +function quickTopicToggle() +{ + theme()->addInlineJavascript(' + document.getElementById("quicktopic_subject").onfocus = function() { + let quicktopicbox = document.getElementById("quicktopicbox"); + let isVisible = quicktopicbox && (quicktopicbox.style.display !== "none" && quicktopicbox.offsetHeight !== 0); + + if (!isVisible) + { + quicktopicbox.slideDown(); + } + };', true); +} From 2fc0d83096adf490e66d5e372d7c4ecb025a6471 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 09:59:52 -0500 Subject: [PATCH 02/10] ! drafts in QR/QT should use standard template. Need new event in MessageIndex.php --- .../AdminController/ManageDraftsModule.php | 2 +- sources/ElkArte/Controller/Display.php | 10 +- sources/ElkArte/Modules/Drafts/Display.php | 32 ++--- .../ElkArte/Modules/Drafts/MessageIndex.php | 129 ++++++++++++++++++ 4 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 sources/ElkArte/Modules/Drafts/MessageIndex.php diff --git a/sources/ElkArte/AdminController/ManageDraftsModule.php b/sources/ElkArte/AdminController/ManageDraftsModule.php index 56bba07388..64952cf914 100644 --- a/sources/ElkArte/AdminController/ManageDraftsModule.php +++ b/sources/ElkArte/AdminController/ManageDraftsModule.php @@ -48,7 +48,7 @@ public static function addCoreFeature(&$core_features) 'setting_callback' => static function ($value) { require_once(SUBSDIR . '/ScheduledTasks.subs.php'); toggleTaskStatusByName('remove_old_drafts', $value); - $modules = array('admin', 'post', 'display', 'profile', 'personalmessage'); + $modules = ['admin', 'post', 'display', 'profile', 'personalmessage', 'messageindex']; // Enabling, let's register the modules and prepare the scheduled task if ($value) diff --git a/sources/ElkArte/Controller/Display.php b/sources/ElkArte/Controller/Display.php index 7ea99e6cd1..6d5b14655e 100644 --- a/sources/ElkArte/Controller/Display.php +++ b/sources/ElkArte/Controller/Display.php @@ -247,8 +247,16 @@ public function action_display() 'width' => '100%', 'smiley_container' => 'smileyBox_message', 'bbc_container' => 'bbcBox_message', - // We do XML preview here. + // We submit/switch to full post page for the preview 'preview_type' => 1, + 'buttons' => [ + 'more' => [ + 'type' => 'submit', + 'name' => 'more_options', + 'value' => $txt['post_options'], + 'options' => '' + ] + ], ]; // Load the template basics now as template_layers is requested by the prepare_context event diff --git a/sources/ElkArte/Modules/Drafts/Display.php b/sources/ElkArte/Modules/Drafts/Display.php index 042b0a2fef..bdf2ed572d 100644 --- a/sources/ElkArte/Modules/Drafts/Display.php +++ b/sources/ElkArte/Modules/Drafts/Display.php @@ -23,7 +23,7 @@ /** * Class \ElkArte\Modules\Drafts\Display * - * Enables draft functions for the Display.controller page (quick reply) + * Enables draft functions for the Display Controller (quick reply) */ class Display extends AbstractModule { @@ -106,24 +106,24 @@ public function prepare_context($use_quick_reply, &$editorOptions, $board) id_draft: ' . (empty($context['id_draft']) ? 0 : $context['id_draft']) . ' }'; - $context['shortcuts_text'] = $txt['shortcuts_drafts']; - - $editorOptions['buttons'] = $editorOptions['buttons'] ?? []; - $editorOptions['hidden_fields'] = $editorOptions['hidden_fields'] ?? []; + loadJavascriptFile('editor/drafts.plugin.js', ['defer' => true]); + } - $editorOptions['buttons'][] = [ - 'name' => 'save_draft', - 'value' => $txt['draft_save'], - 'options' => 'onclick="return confirm(' . JavaScriptEscape($txt['draft_save_note']) . ') && submitThisOnce(this);" accesskey="d"', - ]; + // No need to show this on the quick reply area, normally set as $txt['shortcuts_drafts'] + $context['shortcuts_text'] = ''; - $editorOptions['hidden_fields'][] = [ - 'name' => 'id_draft', - 'value' => empty($context['id_draft']) ? 0 : $context['id_draft'], - ]; + $editorOptions['buttons'] = $editorOptions['buttons'] ?? []; + array_unshift($editorOptions['buttons'], [ + 'name' => 'save_draft', + 'value' => $txt['draft_save'], + 'options' => 'onclick="return confirm(' . JavaScriptEscape($txt['draft_save_note']) . ') && submitThisOnce(this);" accesskey="d"', + ]); - loadJavascriptFile('editor/drafts.plugin.js', ['defer' => true]); - } + $editorOptions['hidden_fields'] = $editorOptions['hidden_fields'] ?? []; + $editorOptions['hidden_fields'][] = [ + 'name' => 'id_draft', + 'value' => empty($context['id_draft']) ? 0 : $context['id_draft'], + ]; } } } diff --git a/sources/ElkArte/Modules/Drafts/MessageIndex.php b/sources/ElkArte/Modules/Drafts/MessageIndex.php new file mode 100644 index 0000000000..ebf9acf84d --- /dev/null +++ b/sources/ElkArte/Modules/Drafts/MessageIndex.php @@ -0,0 +1,129 @@ + true]); + } + + // Hide this for quick topic + $context['shortcuts_text'] = ''; + + $editorOptions['buttons'] = $editorOptions['buttons'] ?? []; + array_unshift($editorOptions['buttons'], [ + 'name' => 'save_draft', + 'value' => $txt['draft_save'], + 'options' => 'onclick="return confirm(' . JavaScriptEscape($txt['draft_save_note']) . ') && submitThisOnce(this);" accesskey="d"', + ]); + + $editorOptions['hidden_fields'] = $editorOptions['hidden_fields'] ?? []; + $editorOptions['hidden_fields'][] = [ + 'name' => 'id_draft', + 'value' => empty($context['id_draft']) ? 0 : $context['id_draft'], + ]; + } + } +} From f64c6ac273aacd53228251a64aa6f7e2a91df3a1 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:01:43 -0500 Subject: [PATCH 03/10] ! Add a more options button to QR QT --- sources/ElkArte/Controller/Post.php | 19 ++-- themes/default/Display.template.php | 132 +++++++++++++--------------- themes/default/Post.template.php | 2 +- 3 files changed, 71 insertions(+), 82 deletions(-) diff --git a/sources/ElkArte/Controller/Post.php b/sources/ElkArte/Controller/Post.php index a810e89982..1c5cd40c81 100644 --- a/sources/ElkArte/Controller/Post.php +++ b/sources/ElkArte/Controller/Post.php @@ -245,7 +245,7 @@ protected function _beforePrepareContext() is_not_guest(); } - // By default the reply will be approved... + // By default, the reply will be approved... $context['becomes_approved'] = true; if ($this->_topic_attributes['id_member'] != $this->user->id) { @@ -352,10 +352,10 @@ protected function _beforePrepareContext() * Get the message setup for ... * * - Sets up the form for preview / modify / new message status. Items - * such as icons, text, etc + * such as icons, text, etc. * - Look if a new topic was posted while working on this prose * - Shows the message preview if requested - * - triggers prepare_modifying, prepare_editing, prepare_posting + * - Triggers prepare_modifying, prepare_editing, prepare_posting */ protected function _generatingMessage() { @@ -441,6 +441,7 @@ private function _previewPost($msg, $topic, $message, $subject) global $txt, $modSettings, $context; $preview = $this->_req->getPost('preview', 'isset', false); + $more_options = $this->_req->getPost('more_options', 'isset', false); $ns = $this->_req->getPost('ns', 'isset', false); $notify = $this->_req->getPost('notify', 'intval', 0); $quote = $this->_req->getRequest('quote', 'intval', 0); @@ -450,8 +451,8 @@ private function _previewPost($msg, $topic, $message, $subject) $icon = $this->_req->getPost('icon', 'trim', 'xx'); $msg_id = 0; - // Validate inputs. - if (!$this->_post_errors->hasErrors()) + // Validate inputs if they are not just moving the full post form (from QR / QT) + if (!$more_options && !$this->_post_errors->hasErrors()) { // This means they didn't click Post and get an error. $really_previewing = true; @@ -496,7 +497,7 @@ private function _previewPost($msg, $topic, $message, $subject) $this->user->name = $context['name']; } - // Only show the preview stuff if they hit Preview. + // Only show the preview stuff if they really hit Preview, not post, not more options if ($really_previewing) { $this->_setupPreviewContext(!$ns); @@ -697,7 +698,7 @@ private function _setupPreviewContext($ns) // Protect any CDATA blocks. if ($this->getApi() === 'xml') { - $context['preview_message'] = strtr($context['preview_message'], array(']]>' => ']]]]>')); + $context['preview_message'] = strtr($context['preview_message'], [']]>' => ']]]]>']); } } @@ -873,7 +874,7 @@ protected function _finalizePage() * - Requires various permissions depending on the action. * - Handles attachment, post, and calendar saving. * - Sends off notifications, and allows for announcements and moderation. - * accessed from ?action=post2. + * - Accessed from ?action=post2. * - Triggers events associated with the actual posting * - prepare_save_post, save_replying, save_new_topic, save_modify * - before_save_post, pre_save_post, after_save_post @@ -919,7 +920,7 @@ public function action_post2() $topic_info = []; // Previewing? Go back to start. - if (isset($_REQUEST['preview'])) + if (isset($_REQUEST['preview']) || isset($_POST['more_options'])) { return $this->action_post(); } diff --git a/themes/default/Display.template.php b/themes/default/Display.template.php index e3c9528d64..6e4df352b2 100644 --- a/themes/default/Display.template.php +++ b/themes/default/Display.template.php @@ -46,29 +46,19 @@ function template_messages_informations_above() // Show the topic information - icon, subject, stats, etc. echo '
-
+
', $context['subject'], ' - ', $context['topic_starter_name'], ' + ', sprintf($txt['topic_started_by'], $context['topic_starter_name']), ' · ', $context['topic_start_time'], ' · ', $context['num_views_text'], ' - - ', - empty($context['links']['go_prev']) ? '' : '' . $txt['previous_next_back'] . '', - empty($context['links']['go_next']) ? '' : ' - ' . $txt['previous_next_forward'] . '', - empty($context['links']['derived_from']) ? '' : ' - ' . sprintf($txt['topic_derived_from'], '' . Util::shorten_text($context['topic_derived_from']['subject'], empty($modSettings['subject_length']) ? 32 : $modSettings['subject_length'])) . '', - ' -
-
'; + '; if (!empty($settings['display_who_viewing']) || !empty($context['topic_redirected_from'])) { - echo ' -
'; - if (!empty($settings['display_who_viewing'])) { echo ' @@ -77,12 +67,12 @@ function template_messages_informations_above() // Show just numbers...? if ($settings['display_who_viewing'] == 1) { - echo count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members']; + echo '', count($context['view_members']), ' ', count($context['view_members']) === 1 ? $txt['who_member'] : $txt['members']; } // Or show the actual people viewing the topic? else { - echo empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); + echo '', empty($context['view_members_list']) ? '0 ' . $txt['members'] : implode(', ', $context['view_members_list']) . (empty($context['view_num_hidden']) || $context['can_moderate_forum'] ? '' : ' (+ ' . $context['view_num_hidden'] . ' ' . $txt['hidden'] . ')'); } // Now show how many guests are here too. @@ -98,12 +88,16 @@ function template_messages_informations_above() ' . sprintf($txt['no_redir'], '' . $context['topic_redirected_from']['subject'] . ''), ' '; } - - echo ' -
'; } echo ' + ', + empty($context['links']['go_prev']) ? '' : '' . $txt['previous_next_back'] . '', + empty($context['links']['go_next']) ? '' : ' - ' . $txt['previous_next_forward'] . '', + empty($context['links']['derived_from']) ? '' : ' - ' . sprintf($txt['topic_derived_from'], '' . Util::shorten_text($context['topic_derived_from']['subject'], empty($modSettings['subject_length']) ? 32 : $modSettings['subject_length'])) . '', ' + +
+
'; } @@ -307,6 +301,14 @@ function template_messages() } } +/** + * This function is responsible for rendering the key information section of a message. + * + * @param array $message The message data. + * @param bool $ignoring Whether the message is being ignored. + * @param bool $above Indicates if the key info section should be rendered above the message body. + * @return void + */ function template_keyinfo($message, $ignoring, $above = false) { global $context, $settings, $options, $txt; @@ -383,8 +385,7 @@ function template_quickreply_below() ', $txt['quick_reply'], '
-
-
'; +
'; if (empty($options['hide_poster_area'])) { @@ -394,84 +395,71 @@ function template_quickreply_below() // Make a postarea similar to post echo ' -
-
-

', $txt['reply'], '

-
-
- - - - - - - - - - - '; +
+
+

', $txt['reply'], '

+
+
+ + + + + + + + + + + '; // Guests just need more. if ($context['user']['is_guest']) { echo ' -
-
- - -
- - -
'; +
+
+ + +
+ + +
'; } // Is visual verification enabled? if (!empty($context['require_verification'])) { - template_verification_controls($context['visual_verification_id'], ' - ' . $txt['verification'] . ':', '
'); + template_verification_controls($context['visual_verification_id'], '' . $txt['verification'] . ':', '
'); } echo ' - ', template_control_richedit($context['post_box_name']); + ', template_control_richedit($context['post_box_name']); echo ' - ', $context['is_locked'] ? '

' . $txt['quick_reply_warning'] . '

' : '', - $context['oldTopicError'] ? '

' . sprintf($txt['error_old_topic'], $modSettings['oldTopicDays']) . '

' : '', ' - ', $context['can_reply_approved'] ? '' : '

' . $txt['wait_for_approval'] . '

'; + ', $context['is_locked'] ? '

' . $txt['quick_reply_warning'] . '

' : '', + $context['oldTopicError'] ? '

' . sprintf($txt['error_old_topic'], $modSettings['oldTopicDays']) . '

' : '', ' + ', $context['can_reply_approved'] ? '' : '

' . $txt['wait_for_approval'] . '

'; echo ' -
- - '; - - // Draft save button? - if (!empty($context['drafts_save'])) - { - echo ' - - '; - } - - echo ' -
'; +
', + template_control_richedit_buttons($context['post_box_name']), ' +
'; // Show the draft last saved on area if (!empty($context['drafts_save'])) { echo ' -
-   - -
'; +
+   + +
'; } echo ' - -
+
-
-
+
+ '; } diff --git a/themes/default/Post.template.php b/themes/default/Post.template.php index 47d8d02f67..51fcb28624 100644 --- a/themes/default/Post.template.php +++ b/themes/default/Post.template.php @@ -58,7 +58,7 @@ function template_postarea_above() ', empty($context['preview_message']) ? '
' : $context['preview_message'], ' -
', isset($context['current_topic']) ? ' +
', isset($context['current_topic']) ? ' ' : '', '

', $context['page_title'], '

From 9481fdc3dcbf803b8179250cc94326514f497d1c Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:03:01 -0500 Subject: [PATCH 04/10] ! template layer debug was broken, aka someone removed it --- sources/ElkArte/Themes/Templates.php | 32 ++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/sources/ElkArte/Themes/Templates.php b/sources/ElkArte/Themes/Templates.php index a2b5837f0f..831974e6af 100644 --- a/sources/ElkArte/Themes/Templates.php +++ b/sources/ElkArte/Themes/Templates.php @@ -21,12 +21,11 @@ use ElkArte\Errors\Errors; use ElkArte\Exceptions\Exception; use ElkArte\Helper\FileFunctions; +use ElkArte\Helper\HttpReq; use ElkArte\Http\Headers; use ElkArte\Languages\Txt; use Error; use Generator; -use JetBrains\PhpStorm\NoReturn; - /** * Class Templates @@ -262,7 +261,7 @@ protected function requireTemplate($template_name, $style_sheets, $fatal): bool * * @param Error $e */ - #[NoReturn] protected function templateNotFound(Error $e) + protected function templateNotFound(Error $e) { global $context, $txt, $scripturl, $boardurl; @@ -411,7 +410,7 @@ private function printLines(Error $e): void * * @used-by printLines() Prints syntax for template files with errors. * @return Generator Highlighted lines ranging from $min to $max. - * @uses highlight_file() Highlights syntax. + * @uses highlight_file() Highlights syntax. * */ public function getHighlightedLinesFromFile(string $file, int $min, int $max): Generator @@ -466,6 +465,7 @@ public function loadSubTemplate($sub_template_name, $fatal = false) { try { + $this->_templateDebug($sub_template_name, true); $theme_function(); } catch (Error $e) @@ -494,6 +494,30 @@ public function loadSubTemplate($sub_template_name, $fatal = false) ); } } + + $this->_templateDebug($sub_template_name); + } + + /** + * Are we showing debugging for templates? Just make sure not to do it before the doctype... + * + * @param bool $start + * @param string $sub_template_name + */ + private function _templateDebug($sub_template_name, $start = false) + { + $req = HttpReq::instance(); + + if ($req->isSet('debug') + && $sub_template_name !== 'init' + && ob_get_length() > 0 + && empty($req->getRequest('xml')) + && empty($req->getRequest('api')) + && allowedTo('admin_forum')) + { + echo ' +
---- ', $sub_template_name, ' ', ($start ? 'starts' : 'ends'), ' ----
'; + } } /** From f72a8e15d624b36240ccd4051a96484b4fa06a23 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:04:22 -0500 Subject: [PATCH 05/10] ! need to pass the height or hidden editors will be to short --- themes/default/GenericControls.template.php | 1 + 1 file changed, 1 insertion(+) diff --git a/themes/default/GenericControls.template.php b/themes/default/GenericControls.template.php index 24f7efc29e..9339e82733 100644 --- a/themes/default/GenericControls.template.php +++ b/themes/default/GenericControls.template.php @@ -41,6 +41,7 @@ function elk_editor() { sceditor.createEx(eTextarea, { style: "', $settings['theme_url'], '/css/', $context['theme_variant_url'], 'jquery.sceditor.wysiwyg', $context['theme_variant'], '.css', CACHE_STALE, '", width: "100%", + height: "', $editor_context['height'], '", autofocus: ', (!empty($context['site_action']) && $context['site_action'] !== 'display') ? 'true' : 'false', ', autofocusEnd: false, startInSourceMode: ', $editor_context['rich_active'] ? 'false' : 'true', ', From fe65adb8b53052021b18b9cb4072b5c582ffca31 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:05:11 -0500 Subject: [PATCH 06/10] ! ajax info bar had no background in core features (missing call) --- themes/default/scripts/admin.js | 1 + 1 file changed, 1 insertion(+) diff --git a/themes/default/scripts/admin.js b/themes/default/scripts/admin.js index ef038f00dd..f2e08810e9 100644 --- a/themes/default/scripts/admin.js +++ b/themes/default/scripts/admin.js @@ -1902,6 +1902,7 @@ function coreFeatures () token_value = xmlDoc.getElementsByTagName('tokens')[0].querySelector('[type="token_var"]').textContent; let message = xmlDoc.getElementsByTagName('messages')[0].getElementsByTagName('message')[0].textContent; + ajax_infobar.isSuccess(); ajax_infobar.changeText(message); } else From fe67668658006feb398b8804a1b009dd55e32748 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:06:42 -0500 Subject: [PATCH 07/10] ! QT css updates ! improved topic header layout --- themes/default/css/index.css | 88 ++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 28 deletions(-) diff --git a/themes/default/css/index.css b/themes/default/css/index.css index 37136ec741..d979591426 100644 --- a/themes/default/css/index.css +++ b/themes/default/css/index.css @@ -2302,7 +2302,7 @@ button[type="submit"].quick_quote_button.hide { .category_boards .board_row { display: grid; min-height: 9rem; - grid-template-columns: [bicon] 7.5rem [info] auto [avatar] 6rem [latest] 32% [stats] minmax(8rem, 12rem) [end]; + grid-template-columns: [bicon] 7.5rem [info] 1fr [avatar] 6.5rem [latest] 30% [stats] minmax(8rem, 12rem) [end]; grid-template-rows: [primary] auto [children] auto; } @@ -2579,7 +2579,7 @@ button[type="submit"].quick_quote_button.hide { } #description_board .generalinfo, #forumposts .generalinfo { - margin: .12em 0 .3rem 0; + margin: 0 0 .3rem 0; padding: .4rem .4rem .4rem 1.2rem; border: 1px solid; } @@ -2597,6 +2597,37 @@ button[type="submit"].quick_quote_button.hide { grid-template-rows: auto; } +/* The quick topic area is like the topic listing, its sits at the top */ +#quicktopic > li { + display: grid; + align-content: center; + grid-template-columns: [topic_icon] 5rem [topic_title] 22.5rem [topic_subject] auto; + grid-template-rows: auto auto; + grid-template-areas: + "topic_icon topic_title topic_subject" + ". editor editor"; +} + +#quicktopicbox .postarea2, #quicktopicbox .postarea { + padding: 1rem; +} + +#quicktopicbox { + grid-area: editor; +} + +#quicktopic_title { + padding: 1rem; + font-weight: 600; + grid-area: topic_title; +} + +#quicktopic_subject { + max-width: 75%; + margin: 1rem; + grid-area: topic_subject; +} + /* Container, holds the topic icon and fred */ .topic_icons { height: 100%; @@ -2877,7 +2908,7 @@ button[type="submit"].quick_quote_button.hide { "postarea"; } -aside.poster, #quickreplybox .poster { +aside.poster, #quickreplybox .poster, #quicktopicbox .poster { grid-area: poster; } @@ -3287,46 +3318,52 @@ aside.poster, #quickreplybox .poster { border-radius: .4rem .4rem 0 0; } -/* The area at the top of each topic page, icon, title, views, links */ #forumposts .category_header { + overflow: hidden; + text-overflow: ellipsis; + display: flex; + align-items: center; +} + +/* The area at the top of each topic page, icon, title, views, links */ +#topic_header.category_header { display: grid; grid-template-columns: 3rem auto 1fr; - grid-template-rows: 4rem auto; + grid-template-rows: 4rem auto auto; grid-template-areas: "hdicon subject subject" - ". stats navigation"; + ". stats navigation" + ". viewing ."; } /* Topic icon and title */ -#forumposts .hdicon { +#topic_header .hdicon { align-self: center; grid-area: hdicon; height: 2.2rem; } -#forumposts .hdicon::before { +#topic_header .hdicon::before { vertical-align: initial; } -#topic_subject { +#topic_header #topic_subject, #boarddescription { align-self: center; grid-area: subject; - font-size: var(--font22); + font-size: var(--font18); } /* Number of views and previous / next links in the category header */ -.nextlinks { +#topic_header .nextlinks { font-size: var(--font15); - margin-left: auto; - white-space: pre; justify-self: end; grid-area: navigation; } -.stats_text { +#topic_header .stats_text { font-size: var(--font13); opacity: .85; - place-self: start center; + place-self: center start; grid-area: stats; } @@ -3336,10 +3373,11 @@ aside.poster, #quickreplybox .poster { border: 1px solid; } -/* Other Topic information, in the description box, above the listing */ +/* Other Topic information */ #whoisviewing, #redirectfrom { font-size: var(--font13); - padding: .4rem 0 .3rem 0; + place-self: center start; + grid-area: viewing; } #redirectfrom { @@ -3606,14 +3644,7 @@ aside.poster, #quickreplybox .poster { margin: 0 auto; } -/* The QR textarea */ -#quickReplyOptions .quickreply { - height: 15rem; - padding: 0 .4rem; - outline: none; -} - -/* The QR buttons may be better suited not being over the topic buttons */ +/* The post area buttons are better suited on the left, not being over other topic buttons */ #post_confirm_buttons { justify-content: flex-start; } @@ -3783,7 +3814,8 @@ ul.post_options li { .shortcuts { font-size: var(--font13); - margin-right: auto; + margin-left: auto; + order: 2; } #mobile .shortcuts { @@ -6153,7 +6185,7 @@ div.labels { max-width: 45%; } - #forumposts .category_header { + #topic_header.category_header { grid-template-columns: auto 1fr; grid-template-rows: 4rem auto; grid-template-areas: @@ -6161,7 +6193,7 @@ div.labels { "stats navigation"; } - #forumposts .views_text, #forumposts .category_header .hdicon { + #topic_header .views_text, #topic_header .hdicon { display: none; } From 0f984d1b13963e4d4dccfa7cacab0c858a8f00b5 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:08:16 -0500 Subject: [PATCH 08/10] ! minor icon tweaks to correct centering and colors --- themes/default/css/icons_svg.css | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/themes/default/css/icons_svg.css b/themes/default/css/icons_svg.css index 58fe6e7583..e2f21b3e57 100644 --- a/themes/default/css/icons_svg.css +++ b/themes/default/css/icons_svg.css @@ -126,10 +126,15 @@ content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='black' viewBox='0 0 32 32'%3E%3Cpath d='M16 32c8.837 0 16-7.163 16-16S24.837 0 16 0 0 7.163 0 16s7.163 16 16 16zm0-29c7.18 0 13 5.82 13 13s-5.82 13-13 13S3 23.18 3 16 8.82 3 16 3z'/%3E%3Cpath d='M20.914 9.914l-2.83-2.83L9.172 16l8.915 8.913 2.828-2.828L14.827 16z'/%3E%3C/svg%3E"); } -/* >> icon From Font Awesome */ -.i-last::before, .i-last_post::before { +/* >> icon From Font Awesome. NOTE adding filer to this allows it to be seen over a drop down menu, instead + define the color here. */ +.i-last::before { content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='black' viewBox='0 0 38 38'%3E%3Cpath d='M.8 29.5c-.4.4-.8.3-.8-.4V3c0-.8.4-1 .8-.5l12.7 12.7.2.3V3c0-.8.4-1 .8-.5l12.7 12.7.2.3v-12c0-.7.5-1.2 1.2-1.2H31c.5 0 1 .5 1 1v25.3c0 .6-.5 1-1 1h-2.4c-.7 0-1.2-.4-1.2-1v-12l-.2.2-12.7 12.7c-.4.4-.8.3-.8-.4V16.6s0 .2-.2.3z'/%3E%3C/svg%3E"); } +/* Same >> icon but NOTE adding a filer allows it to be seen/bleed over a drop down menu, instead define the color here. */ +.i-last_post::before { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%2327A348' viewBox='0 0 38 38'%3E%3Cpath d='M.8 29.5c-.4.4-.8.3-.8-.4V3c0-.8.4-1 .8-.5l12.7 12.7.2.3V3c0-.8.4-1 .8-.5l12.7 12.7.2.3v-12c0-.7.5-1.2 1.2-1.2H31c.5 0 1 .5 1 1v25.3c0 .6-.5 1-1 1h-2.4c-.7 0-1.2-.4-1.2-1v-12l-.2.2-12.7 12.7c-.4.4-.8.3-.8-.4V16.6s0 .2-.2.3z'/%3E%3C/svg%3E"); +} /* Filled Dots, these are simple SVG geometry icons and are not from any font set, use with colorize-xxx class */ @@ -224,7 +229,7 @@ /* Torso From Font Awesome */ .i-user::before { - content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='black' viewBox='0 0 3584 3584'%3E%3Cpath d='M3200 2810q0 240-146 379t-388 139h-1748q-242 0-388-139t-146-379q0-106 7-207t28-218 53-217 86-195 124-162 171-107 223-40q18 0 84 43t149 96 216 96 267 43 267-43 216-96 149-96 84-43q122 0 223 40t171 107 124 162 86 195 53 217 28 218 7 207z m-640-1786q0 318-225 543t-543 225-543-225-225-543 225-543 543-225 543 225 225 543z'/%3E%3C/svg%3E"); + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='black' viewBox='-4 2 28 32'%3E%3Cpath d='M22.857 24.982c0 2.607-1.714 4.732-3.804 4.732H3.803c-2.089 0-3.804-2.125-3.804-4.732 0-4.696 1.161-10.125 5.839-10.125a7.972 7.972 0 0 0 11.178 0c4.679 0 5.839 5.429 5.839 10.125zM18.286 9.143c0 3.786-3.071 6.857-6.857 6.857s-6.857-3.071-6.857-6.857 3.071-6.857 6.857-6.857 6.857 3.071 6.857 6.857z'/%3E%3C/svg%3E"); } /* User outline with green +, 2 color icon, no filter*/ @@ -238,7 +243,7 @@ /* Group of users From Font Awesome */ .i-users::before { - content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='black' viewBox='0 0 34 32'%3E%3Cpath d='M10.59 16q-2.894.09-4.733 2.286H3.464Q2 18.286 1 17.563t-1-2.116q0-6.304 2.214-6.304.107 0 .777.375t1.744.76 2.125.383q1.195 0 2.374-.41-.09.66-.09 1.18 0 2.48 1.447 4.57zm19.124 11.375q0 2.143-1.304 3.384T24.946 32H9.34q-2.162 0-3.465-1.24T4.57 27.374q0-.946.064-1.848t.25-1.946.473-1.937.768-1.74 1.107-1.447T8.76 17.5t1.99-.356q.18 0 .768.384t1.304.857 1.91.857 2.412.384 2.41-.384 1.912-.857 1.304-.857.768-.384q1.09 0 1.99.357t1.528.958 1.107 1.446.768 1.74.476 1.94.25 1.945.063 1.847zM11.43 4.57q0 1.894-1.34 3.233t-3.232 1.34-3.232-1.34-1.34-3.232 1.34-3.23T6.858 0t3.232 1.34 1.34 3.23zM24 11.43q0 2.838-2.01 4.847t-4.847 2.01-4.848-2.01-2.01-4.848 2.01-4.85 4.848-2.01 4.848 2.01T24 11.43zm10.286 4.016q0 1.393-1 2.116t-2.464.723H28.43Q26.59 16.09 23.696 16q1.446-2.09 1.446-4.572 0-.518-.09-1.18 1.18.412 2.376.412 1.053 0 2.124-.384t1.74-.76.778-.374q2.214 0 2.214 6.304zM32 4.57q0 1.894-1.34 3.233t-3.23 1.34-3.233-1.34-1.34-3.232 1.34-3.23T27.43 0t3.23 1.34T32 4.57z'/%3E%3C/svg%3E"); + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='black' viewBox='0 0 32 38'%3E%3Cpath d='M10.59 16q-2.894.09-4.733 2.286H3.464Q2 18.286 1 17.563t-1-2.116q0-6.304 2.214-6.304.107 0 .777.375t1.744.76 2.125.383q1.195 0 2.374-.41-.09.66-.09 1.18 0 2.48 1.447 4.57zm19.124 11.375q0 2.143-1.304 3.384T24.946 32H9.34q-2.162 0-3.465-1.24T4.57 27.374q0-.946.064-1.848t.25-1.946.473-1.937.768-1.74 1.107-1.447T8.76 17.5t1.99-.356q.18 0 .768.384t1.304.857 1.91.857 2.412.384 2.41-.384 1.912-.857 1.304-.857.768-.384q1.09 0 1.99.357t1.528.958 1.107 1.446.768 1.74.476 1.94.25 1.945.063 1.847zM11.43 4.57q0 1.894-1.34 3.233t-3.232 1.34-3.232-1.34-1.34-3.232 1.34-3.23T6.858 0t3.232 1.34 1.34 3.23zM24 11.43q0 2.838-2.01 4.847t-4.847 2.01-4.848-2.01-2.01-4.848 2.01-4.85 4.848-2.01 4.848 2.01T24 11.43zm10.286 4.016q0 1.393-1 2.116t-2.464.723H28.43Q26.59 16.09 23.696 16q1.446-2.09 1.446-4.572 0-.518-.09-1.18 1.18.412 2.376.412 1.053 0 2.124-.384t1.74-.76.778-.374q2.214 0 2.214 6.304zM32 4.57q0 1.894-1.34 3.233t-3.23 1.34-3.233-1.34-1.34-3.232 1.34-3.23T27.43 0t3.23 1.34T32 4.57z'/%3E%3C/svg%3E"); } /* Paperclip, from noto, no filter */ @@ -676,7 +681,7 @@ legend::before, .i-caret-down::before { .i-envelope::before, .i-envelope-o::before, .i-comment::before, .i-profile::before, -.i-bell::before, .i-user::before, .i-last_post::before, +.i-bell::before, .i-user::before, .i-mark_read::before, .i-switch-on::before, .i-recycle::before { filter: invert(46%) sepia(84%) saturate(405%) hue-rotate(83deg) brightness(93%) contrast(93%); --hexcode: #27A348; From 3670dc5bfd8fe08889f5ca70aba310fb02fd1e41 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:10:03 -0500 Subject: [PATCH 09/10] ! minor cleanup, txt tweaks --- sources/ElkArte/Languages/Index/English.php | 2 +- sources/ElkArte/Languages/Profile/English.php | 2 +- sources/ElkArte/TopicUtil.php | 5 +---- sources/subs/Drafts.subs.php | 4 ++-- themes/default/GenericHelpers.template.php | 2 +- themes/default/scripts/post.js | 2 +- 6 files changed, 7 insertions(+), 10 deletions(-) diff --git a/sources/ElkArte/Languages/Index/English.php b/sources/ElkArte/Languages/Index/English.php index d87a628220..4d3311d817 100644 --- a/sources/ElkArte/Languages/Index/English.php +++ b/sources/ElkArte/Languages/Index/English.php @@ -818,7 +818,7 @@ $txt['preview_fetch'] = 'Fetching preview...'; $txt['pm_error_while_submitting'] = 'The following error or errors occurred while sending this personal message:'; $txt['warning_while_submitting'] = 'Something happened, review it here:'; -$txt['error_while_submitting'] = 'The message has the following error or errors that must be corrected before continuing:'; +$txt['error_while_submitting'] = 'The message has the following error or errors that must be corrected before posting:'; $txt['error_old_topic'] = 'Warning: this topic has not been posted in for at least %1$d days.
Unless you\'re sure you want to reply, please consider starting a new topic.'; $txt['split_selected_posts'] = 'Selected posts'; diff --git a/sources/ElkArte/Languages/Profile/English.php b/sources/ElkArte/Languages/Profile/English.php index b6050b86b0..aa41ad8b44 100644 --- a/sources/ElkArte/Languages/Profile/English.php +++ b/sources/ElkArte/Languages/Profile/English.php @@ -305,7 +305,7 @@ $txt['return_to_post'] = 'Return to topics after posting by default.'; $txt['no_new_reply_warning'] = 'Don\'t warn on new replies made while posting.'; $txt['recent_pms_at_top'] = 'Show most recent personal messages at top.'; -$txt['wysiwyg_default'] = 'Show WYSIWYG editor on post page by default.'; +$txt['wysiwyg_default'] = 'Start editor in WYSIWYG mode by default.'; $txt['timeformat_default'] = '(Forum Default)'; $txt['timeformat_easy1'] = 'Month Day, Year, HH:MM:SS am/pm'; diff --git a/sources/ElkArte/TopicUtil.php b/sources/ElkArte/TopicUtil.php index ab6951de43..b384873045 100644 --- a/sources/ElkArte/TopicUtil.php +++ b/sources/ElkArte/TopicUtil.php @@ -111,6 +111,7 @@ public static function prepareContext($topics_info, $topic_seen = false, $previe // Decide how many pages the topic should have. $topic_length = $row['num_replies'] + 1; + $pages = ''; if ($topic_length > $messages_per_page) { // We can't pass start by reference. @@ -118,10 +119,6 @@ public static function prepareContext($topics_info, $topic_seen = false, $previe $show_all = !empty($modSettings['enableAllMessages']) && $topic_length < $modSettings['enableAllMessages']; $pages = constructPageIndex('{scripturl}?topic=' . $row['id_topic'] . '.%1$d' . $topicseen, $start, $topic_length, $messages_per_page, true, array('prev_next' => false, 'all' => $show_all)); } - else - { - $pages = ''; - } $row['new_from'] = $row['new_from'] ?? 0; $first_poster_href = getUrl('profile', ['action' => 'profile', 'u' => $row['first_id_member'], 'name' => $row['first_display_name']]); diff --git a/sources/subs/Drafts.subs.php b/sources/subs/Drafts.subs.php index 51783a47a5..b5811e013c 100644 --- a/sources/subs/Drafts.subs.php +++ b/sources/subs/Drafts.subs.php @@ -564,8 +564,8 @@ function saveDraft($draft, $check_last_save = false) * - The core draft feature must be enabled, as well as the pm draft option * - Determines if this is a new or and update to an existing pm draft * - * @param mixed[] $recipientList - * @param mixed[] $draft + * @param array $recipientList + * @param array $draft * @param bool $check_last_save * * @return bool|void diff --git a/themes/default/GenericHelpers.template.php b/themes/default/GenericHelpers.template.php index bc5f172843..2359e96784 100644 --- a/themes/default/GenericHelpers.template.php +++ b/themes/default/GenericHelpers.template.php @@ -121,7 +121,7 @@ function template_select_boards($name, $label = '', $extra = '', $all = false) * 'counter' => (optional) if set, will add a count indicator span in front of the link text * ]] * - * @param mixed[] $button_strip the above definition array + * @param array $button_strip the above definition array * @param string $class overall class to append to "buttonlist no_js" on the list UL * @param array $strip_options = [] of options applied to the outer
    * 'id' => id to use on the UL diff --git a/themes/default/scripts/post.js b/themes/default/scripts/post.js index 82b3d296f3..c6465e0688 100644 --- a/themes/default/scripts/post.js +++ b/themes/default/scripts/post.js @@ -10,7 +10,7 @@ */ /** global: $editor_data, elk_scripturl, elk_session_var, elk_session_id */ -/** global: poll_add, poll_remove, XMLHttpRequest, form_name */ +/** global: poll_add, poll_remove, XMLHttpRequest, form_name, preview_area */ /** * This file contains javascript associated with the posting and previewing From da9ee2e28043f88fb64b67dba4f124b532d4fb97 Mon Sep 17 00:00:00 2001 From: Spuds Date: Sat, 18 May 2024 10:58:49 -0500 Subject: [PATCH 10/10] ! missed a global --- sources/ElkArte/Controller/MessageIndex.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/ElkArte/Controller/MessageIndex.php b/sources/ElkArte/Controller/MessageIndex.php index c92c1c6f7d..48d7ca3cb5 100644 --- a/sources/ElkArte/Controller/MessageIndex.php +++ b/sources/ElkArte/Controller/MessageIndex.php @@ -885,7 +885,7 @@ private function quickTopic(): void */ private function prepareQuickTopic(): void { - global $options, $context; + global $options, $context, $modSettings; if (empty($options['hide_poster_area']) && $options['display_quick_reply']) {