Browse Source

Added Feature - Announcements - Create announcements that is displayed on "top" of the site (last n entries) and in a full list in seperate page

Re-written to not require JavaScript or cookies to view or edit.

Co-Authored-By: Discomrade <[email protected]>
main
PupperWoff 7 years ago
committed by discomrade
parent
commit
4017822218
  1. 1
      composer.json
  2. 155
      inc/announcements.php
  3. 22
      inc/config.php
  4. 35
      inc/mod/pages.php
  5. 18
      install.php
  6. 13
      install.sql
  7. 2
      mod.php
  8. 24
      stylesheets/style.css
  9. 21
      templates/announcements.html
  10. 66
      templates/announcements_list.html
  11. 0
      templates/generated/.gitkeep
  12. 2
      templates/generic_page.html
  13. 2
      templates/index.html
  14. 28
      templates/main.js
  15. 3
      templates/mod/dashboard.html
  16. 2
      templates/thread.html
  17. 4
      tools/rebuild.php

1
composer.json

@ -26,6 +26,7 @@
"inc/lock.php",
"inc/queue.php",
"inc/polyfill.php",
"inc/announcements.php",
"inc/functions.php"
]
},

155
inc/announcements.php

@ -0,0 +1,155 @@
<?php
// Wrap functions in a class so they don't interfere with normal Tinyboard operations
class Announcements {
static public function new_announcement($announcement, $mod_id = false) {
global $mod, $config;
if ($mod_id === false) {
$mod_id = isset($mod['id']) ? $mod['id'] : -1;
}
$query = prepare("INSERT INTO ``announcements`` VALUES (NULL, :mod, :time, :text)");
$query->bindValue(':mod', $mod_id);
$query->bindValue(':time', time());
if ($announcement !== '') {
$announcement = escape_markup_modifiers($announcement);
markup($announcement);
$query->bindValue(':text', $announcement);
} else
error(sprintf($config['error']['required'], "Announcement"));
$query->execute() or error(db_error($query));
modLog("Created a new announcement: " . utf8tohtml($announcement));
self::buildAnnouncements();
}
static public function edit_announcement($id, $announcement) {
global $mod, $config;
$query = prepare(sprintf("UPDATE ``announcements`` SET `text` = :text WHERE `id` = %d", (int)$id));
if ($announcement !== '') {
$teannouncementxt = escape_markup_modifiers($announcement);
markup($announcement);
$query->bindValue(':text', $announcement);
} else
error(sprintf($config['error']['required'], "Announcement"));
$query->execute() or error(db_error($query));
modLog("Edited announcement #" . $id . " - New Text: " . utf8tohtml($announcement));
self::buildAnnouncements();
}
static public function delete_announcement($id) {
$query = prepare(sprintf("DELETE FROM ``announcements`` WHERE `id` = %d", (int)$id));
$query->execute() or error(db_error($query));
modLog("Deleted announcement #" . $id);
self::buildAnnouncements();
}
static public function buildAnnouncements() {
self::buildShortAnnouncementTable();
self::buildAnnouncementPages();
}
static public function buildShortAnnouncementTable() {
global $config;
$count = $config['announcements']['show_count'];
$query = query("SELECT `text`,`date` FROM ``announcements`` ORDER BY `date` DESC" . (($count === false)?"":" LIMIT " . (int)$count)) or error(db_error($query));
$announcements = $query->fetchAll(PDO::FETCH_ASSOC);
foreach ($announcements as &$announce) {
$announce['date_formated'] = strftime($config['announcements']['date_format'], $announce['date']);
}
$announcements_short = Element('announcements.html', array(
'announcements' => $announcements,
));
file_write($config['dir']['home'] . "templates/generated/announcements_short.html", $announcements_short);
}
static public function buildAnnouncementPages() {
global $config;
// Generate page for full list of announcements
if($config['announcements']['page'])
{
// Generate JSON file for full list of announcements
//file_write($config['dir']['home'] . "announcements.json", self::gen_public_json($config['announcements']['date_format'], false));
$query = query("SELECT ``announcements``.* FROM ``announcements``
ORDER BY `date` DESC") or error(db_error($query));
$announcements = $query->fetchAll(PDO::FETCH_ASSOC);
foreach ($announcements as &$announce) {
$announce['date_formated'] = strftime($config['announcements']['date_format'], $announce['date']);
}
// Generate page for full list of announcements
$announcement_page = Element('page.html', array(
'config' => $config,
'mod' => false,
'hide_dashboard_link' => true,
'boardlist' => createBoardList(false),
'title' => _("Announcements"),
'subtitle' => "",
'nojavascript' => true,
'body' => Element('announcements_list.html', array(
'announcements' => $announcements,
'mod' => false,
'token_json' => false,
))
));
file_write($config['dir']['home'] . $config['announcements']['page_html'], $announcement_page);
}
}
/* Might be used later for mobile API
static public function stream_json($out = false, $filter_staff = false, $date_format, $count = false) {
$query = query("SELECT ``announcements``.*, `username` FROM ``announcements``
LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`
ORDER BY `date` DESC" . (($count === false)?"":" LIMIT " . (int)$count)) or error(db_error($query));
$announcements = $query->fetchAll(PDO::FETCH_ASSOC);
$out ? fputs($out, "[") : print("[");
// Last entry for json end check
$end = end($announcements);
foreach ($announcements as &$announce) {
if($filter_staff)
$announce['username'] = '?';
$announce['date_formated'] = strftime($date_format, $announce['date']);
$json = json_encode($announce);
$out ? fputs($out, $json) : print($json);
if ($announce['id'] != $end['id']) {
$out ? fputs($out, ",") : print(",");
}
}
$out ? fputs($out, "]") : print("]");
}
// Returns json content to be written to json file.
static public function gen_public_json($date_format, $count = false) {
ob_start();
self::stream_json(false, true, $date_format, $count);
$out = ob_get_contents();
ob_end_clean();
return $out;
}
*/
};
?>

22
inc/config.php

@ -946,6 +946,26 @@
// Allow unfiltered HTML in board subtitle. This is useful for placing icons and links.
$config['allow_subtitle_html'] = false;
/*
* ====================
* Announcements settings
* ====================
*/
// Show small list of announcements.
$config['announcements']['show'] = true;
// Number of announcements to include in short announcements lists.
$config['announcements']['show_count'] = 3;
// Date format for announcements.
$config['announcements']['date_format'] = '%m/%d/%Y';
// Create full announcements page.
$config['announcements']['page'] = true;
// Filename for file to hold complete list of announcements
$config['announcements']['page_html'] = "announcements.html";
/*
* ====================
* Display settings
@ -1676,6 +1696,8 @@
$config['mod']['public_ban'] = MOD;
// Manage and install themes for homepage
$config['mod']['themes'] = ADMIN;
// Create or delete announcements
$config['mod']['announcements'] = ADMIN;
// Post news entries
$config['mod']['news'] = ADMIN;
// Custom name when posting news

35
inc/mod/pages.php

@ -963,6 +963,41 @@ function mod_page_ip($cip) {
mod_page(sprintf('%s: %s', _('IP'), htmlspecialchars($cip)), 'mod/view_ip.html', $args, $args['hostname'] ?? null);
}
function mod_announcements() {
global $config;
global $mod;
if (!hasPermission($config['mod']['announcements']))
error($config['error']['noaccess']);
// Add, edit, or delete announcement
if (isset($_POST['announcement'], $_POST['id'])) {
if ($_POST['id'] == '-1') {
Announcements::new_announcement($_POST['announcement']);
} else if ($_POST['announcement'] == '') {
Announcements::delete_announcement($_POST['id']);
} else {
Announcements::edit_announcement($_POST['id'], $_POST['announcement']);
}
}
$query = query("SELECT ``announcements``.*, `username` FROM ``announcements``
LEFT JOIN ``mods`` ON ``mods``.`id` = `creator`
ORDER BY `date` DESC") or error(db_error($query));
$announcements = $query->fetchAll(PDO::FETCH_ASSOC);
foreach ($announcements as &$announce) {
$announce['date_formated'] = strftime($config['announcements']['date_format'], $announce['date']);
}
// Display announcement page
mod_page(_('Announcements list'), 'announcements_list.html', array(
'announcements' => $announcements,
'mod' => $mod,
'token' => make_secure_link_token('announcements'),
));
}
function mod_ban() {
global $config;

18
install.php

@ -636,6 +636,14 @@ if (file_exists($config['has_installed'])) {
PRIMARY KEY (`cookie`,`extra`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;') or error(db_error());
case '5.1.4':
query('CREATE TABLE IF NOT EXISTS `announcements` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`creator` int(10) NOT NULL,
`date` int(10) NOT NULL,
`text` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
') or error(db_error());
query('CREATE TABLE IF NOT EXISTS ``filehashes`` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`board` varchar(58) NOT NULL,
@ -878,6 +886,13 @@ if ($step == 0) {
'required' => true,
'message' => 'vichan does not have permission to create directories (boards) here. You will need to <code>chmod</code> (or operating system equivalent) appropriately.'
),
array(
'category' => 'File permissions',
'name' => getcwd() . '/templates/generated',
'result' => is_dir('templates/generated') && is_writable('templates/generated'),
'required' => true,
'message' => 'You must give vichan permission to write to the <code>templates/generated</code> directory.'
),
array(
'category' => 'File permissions',
'name' => getcwd() . '/templates/cache',
@ -958,6 +973,9 @@ if ($step == 0) {
$more = $_POST['more'];
unset($_POST['more']);
// Generate empty templates that are assumed to exist by other templates
file_write("templates/generated/announcements_short.html", "");
$instance_config =
'<'.'?php

13
install.sql

@ -17,6 +17,19 @@ SET time_zone = "+00:00";
-- --------------------------------------------------------
--
-- Table structure for table `announcements`
--
CREATE TABLE IF NOT EXISTS `announcements` (
`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`creator` int(10) NOT NULL,
`date` int(10) NOT NULL,
`text` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
-- Table structure for table `antispam`
--

2
mod.php

@ -49,6 +49,8 @@ $pages = array(
'/noticeboard' => 'secure_POST noticeboard', // view noticeboard
'/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard
'/noticeboard/delete/(\d+)' => 'secure noticeboard_delete', // delete from noticeboard
'/announcements' => 'secure_POST announcements', // announcement list
'/edit/(\%b)' => 'secure_POST edit_board', // edit board details
'/new-board' => 'secure_POST new_board', // create a new board

24
stylesheets/style.css

@ -532,6 +532,30 @@ div.blotter {
text-align: center;
}
table.announcements {
text-align: center;
width: 480px;
margin-left: auto;
margin-right: auto;
font-size: 0.8em;
}
table.announcements thead td {
border-bottom: 1px solid rgb(63, 63, 63);
border-bottom: 1px solid rgba(0, 0, 0, .4);
-webkit-background-clip: padding-box; /* for Safari */
background-clip: padding-box; /* for IE9+, Firefox 4+, Opera, Chrome */
}
table.announcements tbody {
text-align: left;
}
table.announcements tbody td:first-child {
text-align: right;
vertical-align: top;
}
table.announcements tfoot td {
text-align: right;
}
table.mod.config-editor {
font-size: 9pt;
width: 100%;

21
templates/announcements.html

@ -0,0 +1,21 @@
<table class="announcements" id="announcements">
<thead>
<tr>
<td colspan="2">&nbsp;</td>
</tr>
</thead>
<tbody id="announcements-body">
{% for announcement in announcements %}
<tr>
<td class="announcement-date">{{ announcement.date_formated }}</td><td class="announcement-content">{{ announcement.text }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="2">
<span id="announcements-show-all">[<a href="/announcements.html" target="_blank">Show All</a>]</span>
</td>
</tr>
</tfoot>
</table>

66
templates/announcements_list.html

@ -0,0 +1,66 @@
<script src='main.js'></script>
<script src='js/jquery.min.js'></script>
<script src='js/mobile-style.js'></script>
{% if mod %}
<center>
<p>To remove an announcement, make it empty then press Update.</p>
<p>You will need to <a href="?/rebuild">{% trans 'Rebuild' %}</a> pages after this to display the new announcements.</p>
</center>
<table id="announcements-list" class="announcements-list modlog">
<thead>
<tr>
<th>Date</th>
<th>Announcement</th>
<th>Staff</th>
<th width='100px'>Action</th>
</tr>
</thead>
<tbody>
<form style="margin:0" action="" method="post">
<input type="hidden" name="id" value="-1">
{% if token %}
<input type="hidden" name="token" value="{{ token }}">
{% endif %}
<tr>
<td><center>-&gt;</center></td>
<td><input type="text" name="announcement" placeholder="Add a new announcement..." required="required" style="width:100%" value=""></td>
<td>-</td>
<td><input type="submit" value="Add new"</input></td>
</tr>
</form>
{% for announcement in announcements %}
<form style="margin:0" action="" method="post">
<input type="hidden" name="id" value="{{ announcement.id }}">
{% if token %}
<input type="hidden" name="token" value="{{ token }}">
{% endif %}
<tr>
<td><center>{{ announcement.date_formated }}</center></td>
<td><input type="text" name="announcement" style="width:100%" value="{{ announcement.text }}"></td>
<td><a href='?/new_PM/{{ announcement.username }}'>{{ announcement.username }}</td>
<td><input type="submit" value="Update"</input></td>
</tr>
</form>
{% endfor %}
</tbody>
</table>
{% else %}
<table id="announcements-list" class="announcements-list modlog">
<thead>
<tr>
<th>Date</th>
<th>Announcement</th>
</tr>
</thead>
<tbody>
{% for announcement in announcements %}
<tr>
<td>{{ announcement.date_formated }}</td>
<td>{{ announcement.text }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}

0
templates/generated/.gitkeep

2
templates/generic_page.html

@ -23,6 +23,8 @@
{% include 'attention_bar.html' %}
{% include 'post_form.html' %}
{% if config.announcements.show %}{% include 'generated/announcements_short.html' %}{% endif %}
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
<hr />
<form name="postcontrols" action="{{ config.post_url }}" method="post">

2
templates/index.html

@ -55,6 +55,8 @@
</div>
{% endif %}
{% if config.announcements.show %}{% include 'generated/announcements_short.html' %}{% endif %}
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
<hr />
<span id="index-links-top" class="index-links">

28
templates/main.js

@ -369,6 +369,12 @@ function init() {
if (window.location.hash.indexOf('q') != 1 && window.location.hash.substring(1))
highlightReply(window.location.hash.substring(1));
{% endverbatim %}
{% if config.announcements.show %}
init_announcements();
{% endif %}
{% verbatim %}
}
var RecaptchaOptions = {
@ -405,3 +411,25 @@ var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(sc, s);
{% endif %}
{% if config.announcements.show %}
function init_announcements() {
$("#announcements-show-all").prepend('<span>[<a id="toggle-announcements">Hide</a>]</span>');
var hideAnnouncements = (localStorage.getItem('hideAnnouncements') !== null);
toggleAnnouncementList(hideAnnouncements);
$("#toggle-announcements").click(function() {
var hide = (localStorage.getItem('hideAnnouncements') !== null);
toggleAnnouncementList(!hide);
if (hide) {
localStorage.removeItem('hideAnnouncements');
} else {
localStorage.setItem('hideAnnouncements', true);
}
});
}
function toggleAnnouncementList(hide) {
$("#announcements-body").attr("hidden", hide);
$("#toggle-announcements").text(hide?"Show":"Hide");
}
{% endif %}

3
templates/mod/dashboard.html

@ -72,6 +72,9 @@
{% if unread_pms > 0 %}<strong>{%endif %}({{ unread_pms }} unread){% if unread_pms > 0 %}</strong>{%endif %}
</a>
</li>
{% if mod|hasPermission(config.mod.announcements) %}
<li><a href="?/announcements">{% trans 'Announcements' %}</a></li>
{% endif %}
</ul>
</fieldset>

2
templates/thread.html

@ -50,6 +50,8 @@
{% include 'post_form.html' %}
{% if config.announcements.show %}{% include 'generated/announcements_short.html' %}{% endif %}
{% if config.global_message %}<hr /><div class="blotter">{{ config.global_message }}</div>{% endif %}
<hr />
<div>

4
tools/rebuild.php

@ -21,6 +21,7 @@
require dirname(__FILE__) . '/inc/cli.php';
require_once("inc/announcements.php");
require_once("inc/bans.php");
$start = microtime(true);
@ -99,6 +100,9 @@ foreach($boards as &$board) {
}
}
// Generate announcement files
Announcements::buildAnnouncements();
if(!$options['quiet'])
printf("Complete! Took %g seconds\n", microtime(true) - $start);

Loading…
Cancel
Save