diff --git a/README.md b/README.md index fa8a674..c7affb1 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,12 @@ A news page that shows the latest topics from chosen forums, shortened to a set ## Features - A **News** page at `/news`, linked from the board's navigation bar with an icon you choose. -- Shows the first post of the newest topics in the forums you pick, with a set number of news per page and pages. -- Shortens long news to a set number of characters, with a "Read full news" link. Formatting is kept and no tag is cut in half. -- Visitors can filter the news by forum and by month, and open a single news item. -- Optional poster information (avatar, rank, profile fields), post buttons, attachments and moved topics. +- Shows the first post of the newest topics in the forums you pick, as news cards: title, a byline with author, date and forum, the text, and one line with "Read more", the comment count and "View topic". +- Shortens long news to a set number of characters, with "Read more" only when the text was cut. Formatting is kept and no tag is cut in half. +- Optionally shows each post's first image above its text, from the text or its attachments. +- Visitors can filter the news by forum and by month; the active filters show under the filter bar and can be removed one at a time. +- A single news item has its own page, with a link back to all news and "Reply" for readers who may reply. +- Optional poster information (avatar, rank, post count, profile fields), post buttons, attachments and moved topics. - Shows in **Who is online** when someone is reading the news. - Settings under **ACP → Extensions → Newspage**. diff --git a/acp/main_module.php b/acp/main_module.php index caa45e4..9b27453 100644 --- a/acp/main_module.php +++ b/acp/main_module.php @@ -63,6 +63,7 @@ public function main($id, $mode) 'SETTING_NEWS_USER_INFO' => (bool) $config['news_user_info'], 'SETTING_NEWS_POST_BUTTONS' => (bool) $config['news_post_buttons'], 'SETTING_NEWS_ATTACH_SHOW' => (bool) $config['news_attach_show'], + 'SETTING_NEWS_LEAD_IMAGE' => !empty($config['news_lead_image']), )); } } diff --git a/adm/style/acp_newspage_settings.html b/adm/style/acp_newspage_settings.html index f7c19f9..c3b291e 100644 --- a/adm/style/acp_newspage_settings.html +++ b/adm/style/acp_newspage_settings.html @@ -46,6 +46,10 @@

{L_ACP_NEWSPAGE}


{L_NEWS_POST_BUTTONS_EXPLAIN}
checked="checked" />
+
+

{L_NEWS_LEAD_IMAGE_EXPLAIN}
+
checked="checked" />
+

{L_NEWS_ATTACH_SHOW_EXPLAIN}
checked="checked" />
diff --git a/controller/settings.php b/controller/settings.php index 8ca461f..f0cc72c 100644 --- a/controller/settings.php +++ b/controller/settings.php @@ -74,5 +74,6 @@ public function save_settings() $this->config->set('news_attach_show', $this->request->variable('news_attach_show', false)); $this->config->set('news_cat_show', $this->request->variable('news_cat_show', false)); $this->config->set('news_archive_show', $this->request->variable('news_archive_show', false)); + $this->config->set('news_lead_image', $this->request->variable('news_lead_image', false)); } } diff --git a/docs/images/screenshots/README.md b/docs/images/screenshots/README.md index 1fa46f8..cbb11bc 100644 --- a/docs/images/screenshots/README.md +++ b/docs/images/screenshots/README.md @@ -1,20 +1,26 @@ # NV Newspage screenshots -Captured with [phpbb-release-tools](https://github.com/phpbbmodders/phpbb-release-tools)’ `phpbb-test-board/screenshots.py` on a disposable phpBB 3.3.19 board, with three example news topics. Poster information and the post buttons are turned off; the other settings are the defaults. +Captured with [phpbb-release-tools](https://github.com/phpbbmodders/phpbb-release-tools)’ `phpbb-test-board/screenshots.py` on a disposable phpBB 3.3.19 board, with four example news topics. Unless a caption says otherwise, **Show the first image above the news** is on, and poster information and the post buttons are off; the other settings are the defaults. ## News page News page +## Filtered by forum + +The chosen forum shows in its filter button and under the bar, where it can be removed. + +News page filtered by forum + ## With poster information and post buttons **Show user information** and **Show post-related buttons** on, seen by the admin, who gets every button. News page with poster information and post buttons -## Shortened news +## Shortened news, no lead image -**Shorten news text** set to 120 characters. Longer news ends with "…", and **Read full news** opens the whole post. +**Shorten news text** set to 120 characters and **Show the first image above the news** off. Longer news ends with "…", and **Read more** opens the whole post. News page with shortened news @@ -26,6 +32,8 @@ Captured with [phpbb-release-tools](https://github.com/phpbbmodders/phpbb-releas ## A single news item +Seen by the admin, who can reply. + A single news item ## ACP settings diff --git a/docs/images/screenshots/newspage-acp-settings.png b/docs/images/screenshots/newspage-acp-settings.png index b3dfcb0..6d35909 100644 Binary files a/docs/images/screenshots/newspage-acp-settings.png and b/docs/images/screenshots/newspage-acp-settings.png differ diff --git a/docs/images/screenshots/newspage-news-filtered.png b/docs/images/screenshots/newspage-news-filtered.png new file mode 100644 index 0000000..5c6ce41 Binary files /dev/null and b/docs/images/screenshots/newspage-news-filtered.png differ diff --git a/docs/images/screenshots/newspage-news-full.png b/docs/images/screenshots/newspage-news-full.png index d75a9ac..fef2a19 100644 Binary files a/docs/images/screenshots/newspage-news-full.png and b/docs/images/screenshots/newspage-news-full.png differ diff --git a/docs/images/screenshots/newspage-news-no-filters.png b/docs/images/screenshots/newspage-news-no-filters.png index e639049..141e08a 100644 Binary files a/docs/images/screenshots/newspage-news-no-filters.png and b/docs/images/screenshots/newspage-news-no-filters.png differ diff --git a/docs/images/screenshots/newspage-news-phone.png b/docs/images/screenshots/newspage-news-phone.png index df72f62..d61b30e 100644 Binary files a/docs/images/screenshots/newspage-news-phone.png and b/docs/images/screenshots/newspage-news-phone.png differ diff --git a/docs/images/screenshots/newspage-news-shortened.png b/docs/images/screenshots/newspage-news-shortened.png index e55a42d..882d4a4 100644 Binary files a/docs/images/screenshots/newspage-news-shortened.png and b/docs/images/screenshots/newspage-news-shortened.png differ diff --git a/docs/images/screenshots/newspage-news.png b/docs/images/screenshots/newspage-news.png index 7e64d27..5362d75 100644 Binary files a/docs/images/screenshots/newspage-news.png and b/docs/images/screenshots/newspage-news.png differ diff --git a/docs/images/screenshots/newspage-single.png b/docs/images/screenshots/newspage-single.png index 65a488d..e50350d 100644 Binary files a/docs/images/screenshots/newspage-single.png and b/docs/images/screenshots/newspage-single.png differ diff --git a/language/de/newspage.php b/language/de/newspage.php index 369f9c6..d8d66b0 100644 --- a/language/de/newspage.php +++ b/language/de/newspage.php @@ -28,16 +28,28 @@ 'NEWS_ARCHIVE_OF' => 'Archiv vom %s', 'NEWS_ATTACH_SHOW' => 'Anhänge anzeigen', 'NEWS_ATTACH_SHOW_EXPLAIN' => 'Anhänge die in Beiträge eingefügt wurden, werden immer angezeigt.', - 'NEWS_CAT' => 'Kategorien', + 'NEWS_CAT' => 'Foren', 'NEWS_CAT_SHOW' => 'Erlaube die Foren einzuschränken', 'NEWS_CHAR_LIMIT' => 'News-Einträge kürzen', 'NEWS_CHAR_LIMIT_EXPLAIN' => 'Textlänge, „0“ um Kürzen zu unterbinden', 'NEWS_COMMENTS' => 'Kommentare', - 'NEWS_FILTER_ARCHIVE' => 'Datum filtern', + 'NEWS_COMMENTS_COUNT' => array( + 0 => 'Noch keine Kommentare', + 1 => '%d Kommentar', + 2 => '%d Kommentare', + ), + 'NEWS_ALL' => 'Alle News', + 'NEWS_REPLY' => 'Antworten', + 'NEWS_IN_FORUM' => 'in %s', + 'NEWS_READ_MORE' => 'Weiterlesen', + 'NEWS_VIEW_TOPIC' => 'Thema ansehen', + 'NEWS_FILTER_ARCHIVE' => 'Alle Monate', 'NEWS_FILTER_BY_ARCHIVE' => 'Filter News-Einträge nach Jahr und Monat', 'NEWS_FILTER_BY_CATEGORY' => 'Filter News-Einträge nach Forum', - 'NEWS_FILTER_CATEGORY' => 'Kategorie filtern', + 'NEWS_FILTER_CATEGORY' => 'Alle Foren', 'NEWS_FILTER_REMOVE' => 'Filter entfernen', + 'NEWS_FILTER_CLEAR' => 'Filter zurücksetzen', + 'NEWS_FILTER_SHOWING' => 'Angezeigt', 'NEWS_FORUMS' => 'News-Foren auswählen', 'NEWS_GO_TO_TOPIC' => 'Link zum Thema', 'NEWS_NUMBER' => 'News pro Seite', diff --git a/language/en/info_acp_newspage.php b/language/en/info_acp_newspage.php index b73dbe9..58ecfa3 100644 --- a/language/en/info_acp_newspage.php +++ b/language/en/info_acp_newspage.php @@ -25,5 +25,7 @@ 'LOG_NEWSPAGE_SETTINGS' => 'Changed Newspage settings', 'NEWS_ICON' => 'News navigation icon', 'NEWS_ICON_EXPLAIN' => 'Font Awesome icon class, for example fa-bullhorn or fa-rss. phpBB 3.3 uses Font Awesome 4.7 and phpBB 4.0 uses Font Awesome 6, so choose a name both have.', + 'NEWS_LEAD_IMAGE' => 'Show the first image above the news', + 'NEWS_LEAD_IMAGE_EXPLAIN' => 'Shows the first image of each news post, from the text or its attachments, above the text, and takes it out of the text. A single news item still shows the whole post.', 'NEWS_ICON_INVALID' => 'Enter a Font Awesome icon class in the format fa-bullhorn.', )); diff --git a/language/en/newspage.php b/language/en/newspage.php index e5cda0d..8be9f58 100644 --- a/language/en/newspage.php +++ b/language/en/newspage.php @@ -28,16 +28,28 @@ 'NEWS_ARCHIVE_OF' => 'Archive %s', 'NEWS_ATTACH_SHOW' => 'Show attachments', 'NEWS_ATTACH_SHOW_EXPLAIN' => 'Inline attachments will always be displayed.', - 'NEWS_CAT' => 'Categories', + 'NEWS_CAT' => 'Forums', 'NEWS_CAT_SHOW' => 'Allow to filter by forums', 'NEWS_CHAR_LIMIT' => 'Shorten news text', 'NEWS_CHAR_LIMIT_EXPLAIN' => 'Text length, “0” to disable shortening', 'NEWS_COMMENTS' => 'Comments', - 'NEWS_FILTER_ARCHIVE' => 'Filter date', + 'NEWS_COMMENTS_COUNT' => array( + 0 => 'No comments yet', + 1 => '%d comment', + 2 => '%d comments', + ), + 'NEWS_ALL' => 'All news', + 'NEWS_REPLY' => 'Reply', + 'NEWS_IN_FORUM' => 'in %s', + 'NEWS_READ_MORE' => 'Read more', + 'NEWS_VIEW_TOPIC' => 'View topic', + 'NEWS_FILTER_ARCHIVE' => 'All months', 'NEWS_FILTER_BY_ARCHIVE' => 'Filter news by date', 'NEWS_FILTER_BY_CATEGORY' => 'Filter news by forum', - 'NEWS_FILTER_CATEGORY' => 'Filter forum', + 'NEWS_FILTER_CATEGORY' => 'All forums', 'NEWS_FILTER_REMOVE' => 'Remove filter', + 'NEWS_FILTER_CLEAR' => 'Clear filters', + 'NEWS_FILTER_SHOWING' => 'Showing', 'NEWS_FORUMS' => 'Select News-Forums', 'NEWS_GO_TO_TOPIC' => 'Link to topic', 'NEWS_NUMBER' => 'News per page', diff --git a/lead_image.php b/lead_image.php new file mode 100644 index 0000000..13a11f7 --- /dev/null +++ b/lead_image.php @@ -0,0 +1,111 @@ + $html, 'src' => '', 'alt' => ''); + if ($html === '' || stripos($html, 'postimage') === false) + { + return $none; + } + + $document = new \DOMDocument(); + $previous = libxml_use_internal_errors(true); + $loaded = $document->loadHTML('
' . $html . '
', LIBXML_NONET); + libxml_clear_errors(); + libxml_use_internal_errors($previous); + if (!$loaded) + { + return $none; + } + + $wrapper = $document->getElementsByTagName('div')->item(0); + $image = null; + foreach ($wrapper->getElementsByTagName('img') as $candidate) + { + if (in_array('postimage', preg_split('/\s+/', $candidate->getAttribute('class')), true) && $candidate->getAttribute('src') !== '') + { + $image = $candidate; + break; + } + } + if ($image === null) + { + return $none; + } + + $src = $image->getAttribute('src'); + $alt = $image->getAttribute('alt'); + $this->remove_image($image, $wrapper); + + // Line breaks that came after the image would now start the text + while ($wrapper->firstChild !== null && ($wrapper->firstChild->nodeName === 'br' || ($wrapper->firstChild instanceof \DOMText && trim($wrapper->firstChild->nodeValue) === ''))) + { + $wrapper->removeChild($wrapper->firstChild); + } + + $result = ''; + foreach ($wrapper->childNodes as $node) + { + $result .= $document->saveHTML($node); + } + + return array('html' => trim($result), 'src' => $src, 'alt' => $alt); + } + + /** + * Remove the image together with the block that only exists to hold it. + * + * @param \DOMElement $image The image + * @param \DOMElement $wrapper The element around the whole post + * @return void + */ + protected function remove_image(\DOMElement $image, \DOMElement $wrapper) + { + // An attachment: drop its whole block, and an inline-attachment wrapper around it + $remove = null; + for ($node = $image->parentNode; $node !== null && !$node->isSameNode($wrapper); $node = $node->parentNode) + { + $classes = $node instanceof \DOMElement ? preg_split('/\s+/', $node->getAttribute('class')) : array(); + if (in_array('inline-attachment', $classes, true) || ($node->nodeName === 'dl' && in_array('file', $classes, true))) + { + $remove = $node; + } + } + + // A linked image: drop the link too + if ($remove === null) + { + $parent = $image->parentNode; + $remove = ($parent->nodeName === 'a' && trim($parent->textContent) === '') ? $parent : $image; + } + + $remove->parentNode->removeChild($remove); + } +} diff --git a/migrations/v14x/lead_image.php b/migrations/v14x/lead_image.php new file mode 100644 index 0000000..5f20ece --- /dev/null +++ b/migrations/v14x/lead_image.php @@ -0,0 +1,35 @@ +config['news_lead_image']); + } + + public static function depends_on() + { + return array('\\phpbbmodders\\newspage\\migrations\\v11x\\acp_settings'); + } + + public function update_data() + { + return array( + array('config.add', array('news_lead_image', 0)), + ); + } +} diff --git a/newspage.php b/newspage.php index a14e36e..150f859 100644 --- a/newspage.php +++ b/newspage.php @@ -158,6 +158,38 @@ protected function get_poster_avatar(array $row) . ' alt="' . utf8_htmlspecialchars($avatar['title']) . '" />'; } + /** + * Take a post's first image out of its text, or else out of its attachment list. + * + * @param string $message Rendered post text, attachments filled in + * @param string[] $attachments Rendered attachments not shown in the text + * @return array 'html' and 'attachments' without the image; 'src' and 'alt' of the + * image, both '' when the post has none + */ + protected function take_lead_image($message, array $attachments) + { + $lead_image = new lead_image(); + $taken = $lead_image->take($message); + $taken['attachments'] = $attachments; + + if ($taken['src'] === '') + { + foreach ($attachments as $key => $attachment) + { + $from_attachment = $lead_image->take($attachment); + if ($from_attachment['src'] !== '') + { + $taken['src'] = $from_attachment['src']; + $taken['alt'] = $from_attachment['alt']; + unset($taken['attachments'][$key]); + break; + } + } + } + + return $taken; + } + /** * @return string */ @@ -309,9 +341,13 @@ public function base() } $sql_array = array( - 'SELECT' => 't.*, p.*, u.*', + 'SELECT' => 't.*, p.*, u.*, f.forum_name', 'FROM' => array(TOPICS_TABLE => 't'), 'LEFT_JOIN' => array( + array( + 'FROM' => array(FORUMS_TABLE => 'f'), + 'ON' => 'f.forum_id = t.forum_id', + ), array( 'FROM' => array(POSTS_TABLE => 'p'), 'ON' => 'p.post_id = t.topic_first_post_id', @@ -347,17 +383,39 @@ public function base() $parse_flags = ($row['enable_bbcode'] ? OPTION_FLAG_BBCODE : 0) | ($row['enable_smilies'] ? OPTION_FLAG_SMILIES : 0) | ($row['enable_magic_url'] ? OPTION_FLAG_LINKS : 0); $message = generate_text_for_display($row['post_text'], $row['bbcode_uid'], $row['bbcode_bitfield'], $parse_flags, true); + // The first image is shown above the text on the news list, if the board wants it. + // Attachments are filled in first, so an image attachment can be that image. + $lead_image = array('src' => '', 'alt' => ''); + $attachments_parsed = false; + if (!$this->news && !empty($this->config['news_lead_image'])) + { + if ($this->auth->acl_get('f_download', $forum_id) && !empty($attachments[$post_id])) + { + parse_attachments($forum_id, $message, $attachments[$post_id], $update_count); + $attachments_parsed = true; + } + $lead_image = $this->take_lead_image($message, $attachments_parsed ? $attachments[$post_id] : array()); + $message = $lead_image['html']; + if ($attachments_parsed) + { + $attachments[$post_id] = $lead_image['attachments']; + } + } + + $shortened = false; if (!$this->news && (int) $this->config['news_char_limit'] > 0) { $trimmer = new message_trimmer(); + $full_message = $message; $message = $trimmer->trim($message, (int) $this->config['news_char_limit']); + $shortened = $message !== $full_message; } if (!$this->auth->acl_get('f_download', $forum_id)) { $display_notice = true; } - else if (!empty($attachments[$post_id])) + else if (!$attachments_parsed && !empty($attachments[$post_id])) { parse_attachments($forum_id, $message, $attachments[$post_id], $update_count); } @@ -482,6 +540,13 @@ public function base() 'EDIT_REASON' => htmlspecialchars($row['post_edit_reason'], ENT_QUOTES, 'UTF-8', false), 'SIGNATURE' => ($row['enable_sig']) ? $row['user_sig'] : '', 'NEWS_COMMENTS' => $this->content_visibility->get_count('topic_posts', $row, $forum_id) - 1, + 'NEWS_COMMENTS_TEXT' => $this->user->lang('NEWS_COMMENTS_COUNT', $this->content_visibility->get_count('topic_posts', $row, $forum_id) - 1), + 'S_NEWS_SHORTENED' => $shortened, + 'U_NEWS_REPLY' => ($this->auth->acl_get('f_reply', $forum_id) && ($row['topic_status'] != ITEM_LOCKED || $this->auth->acl_get('m_edit', $forum_id))) ? append_sid("{$this->root_path}posting.{$this->php_ext}", "mode=reply&t=$topic_id") : '', + 'NEWS_LEAD_IMAGE_SRC' => utf8_htmlspecialchars($lead_image['src']), + 'NEWS_LEAD_IMAGE_ALT' => utf8_htmlspecialchars($lead_image['alt']), + 'NEWS_FORUM_NAME' => isset($row['forum_name']) ? $row['forum_name'] : '', + 'U_NEWS_FORUM' => append_sid("{$this->root_path}viewforum.{$this->php_ext}", 'f=' . $forum_id), 'POSTER_AVATAR' => ($this->user->optionget('viewavatars')) ? $this->get_poster_avatar($row) : '', 'U_POST_AUTHOR' => get_username_string('profile', $poster_id, $row['username'], $row['user_colour'], $row['post_username']), diff --git a/styles/prosilver/template/event/overall_header_head_append.html b/styles/prosilver/template/event/overall_header_head_append.html index 06a8fe3..c0fdd0f 100644 --- a/styles/prosilver/template/event/overall_header_head_append.html +++ b/styles/prosilver/template/event/overall_header_head_append.html @@ -1,4 +1 @@ - - - diff --git a/styles/prosilver/template/event/viewtopic_body_postrow_post_notices_after.html b/styles/prosilver/template/event/viewtopic_body_postrow_post_notices_after.html deleted file mode 100644 index 65d4637..0000000 --- a/styles/prosilver/template/event/viewtopic_body_postrow_post_notices_after.html +++ /dev/null @@ -1,12 +0,0 @@ - -
- - {L_NEWS_POLL}{L_COLON} {postrow.POLL_QUESTION} » {L_NEWS_POLL_GOTO}

- - - {L_NEWS_READ_FULL}
- - {L_NEWS_GO_TO_TOPIC}{L_COLON} {postrow.POST_SUBJECT}
- {L_NEWS_COMMENTS}{L_COLON} {postrow.NEWS_COMMENTS} -
- diff --git a/styles/prosilver/template/newspage_archives_dropdown.html b/styles/prosilver/template/newspage_archives_dropdown.html index 0b91aac..7286130 100644 --- a/styles/prosilver/template/newspage_archives_dropdown.html +++ b/styles/prosilver/template/newspage_archives_dropdown.html @@ -1,11 +1,8 @@ -