MyBB Community Forums

Full Version: MyAlerts: Align full page listing semantics with those of pop-up dialogue + more
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
MyAlerts is a popular plugin for MyBB - essential for some of us. I and a member of the board I administer were not satisfied with its semantics though. In particular:

The semantics of the modal pop-up (accessed by clicking "Alerts" in the header) are different to those of the full listing (at alerts.php, accessed via the modal pop-up by clicking "View Alerts" in the pop-up's footer).

In particular:
  1. The modal pop-up does not affect the (un)read status of alerts, whereas simply visiting the full listing marks all unread alerts as read.
  2. The modal pop-up visually dims read alerts, whereas the full listing does not.
We resolved to bring the semantics of the full listing into alignment with those of the modal pop-up, and, thereafter, to add a "Mark All Read" link to both.

Here is how we did it, in (hopefully) enough detail that you can too. Note that line numbers based on the current GitHub version may differ slightly for you depending on which version you're running. Note also that where "comment out" is indicated, the addition "or delete" should be assumed.

First: aligning the two listings.

In alerts.php, comment out this line (#428 on GitHub):
	MybbStuff_MyAlerts_AlertManager::getInstance()->markRead($readAlerts);
This is the main line that causes unread alerts to be automatically marked as read upon loading the full page listing.

In jscripts/myalerts.js:
  1. Comment out these lines (#18-22 on GitHub):
                    window.setInterval(function () {
                        $.get('xmlhttp.php?action=getNewAlerts', function (data) {
                            $('#latestAlertsListing').prepend(data);
                        });
                    }, myalerts_autorefresh * 1000);
    
    These are the secondary lines that cause unread alerts to be automatically marked as read, but via AJAX, after loading the full listing.
  2. Also comment out this line (line #12 on GitHub):
                bodySelector.on("click", "#getUnreadAlerts", unreadAlertsProxy);
    
    This line calls dodgy code that was predicated on returning unread alerts and dynamically prepending them to the existing list on the full page listing - but the code to prepend them uses the wrong variable identifier, making it ineffective, and - in some cases, on some browsers - prevents the "Check for new alerts" link on the full listing from working.
In inc/plugins/myalerts.php, comment out these lines (#1403-1405 on GitHub):
			MybbStuff_MyAlerts_AlertManager::getInstance()->markRead(
				$toMarkRead
			);
These lines, too, contribute to the problem of unread posts automatically being marked read, although they probably are nullified anyway in this respect given the previous commenting-out of line #12 of jscripts/myalerts.js mentioned above.

In the live stylesheet alerts.css, via the ACP, change:

.alert--read.alert {
    opacity: .5;
}

to:

.alert--read.alert, tr.alert--read {
    opacity: .5;
}

Now, the semantics of the two listings should be aligned. You might now want, though, as we did - given that alerts are no longer automatically marked as read on visiting the full listing - to do this:

Second: adding a "Mark All Read" link.

To the "switch" statement up the top of alerts.php, add the following code:

	case 'mark_all_read':
		myalerts_mark_all_alerts_read($mybb, $lang);
		break;

Also to that file, add the following function:

/**
 * Mark all alerts as read.
 *
 * @param MyBB       $mybb MyBB core object.
 * @param MyLanguage $lang MyBB language system.
 */
function myalerts_mark_all_alerts_read($mybb, $lang)
{
	verify_post_check($mybb->get_input('my_post_key'));

	$alertsList = MybbStuff_MyAlerts_AlertManager::getInstance()->getAlerts(0);

	$alertIds = array();

	if (!empty($alertsList) && is_array($alertsList)) {
		foreach ($alertsList as $alertObject) {
			$alert = parse_alert($alertObject);
			$alertIds[] = $alert['id'];
		}
	}

	MybbStuff_MyAlerts_AlertManager::getInstance()->markRead($alertIds);

	$retLink = $mybb->get_input('ret_link', MyBB::INPUT_STRING);

	if (!empty($retLink) && stripos($retLink, $mybb->settings['bburl']) === 0) {
		$retLink = htmlspecialchars_uni($retLink);
		redirect(
			$retLink,
			$lang->myalerts_marked_all_read_desc,
			$lang->myalerts_marked_all_read_title
		);
	} else {
		redirect(
			'alerts.php?action=alerts',
			$lang->myalerts_marked_all_read_desc,
			$lang->myalerts_marked_all_read_title
		);
	}
}

To inc/languages/english/myalerts.lang.php (or your localised variant) add (potentially localised):

$l['myalerts_page_mark_all_read'] = 'Mark All Read';
$l['myalerts_modal_mark_all_read'] = 'Mark All Read';
$l['myalerts_marked_all_read_title'] = 'Marked All Read';
$l['myalerts_marked_all_read_desc'] = 'All alerts were successfully marked as read.';

To your live templates, via the ACP:

Prepend to these lines in the template myalerts_modal_content:
                    <a href="{$mybb->settings['bburl']}/alerts.php?action=delete_read&amp;my_post_key={$mybb->post_code}&amp;ret_link={$myalerts_return_link}"
                       onclick="return confirm('{$lang->myalerts_modal_delete_read_confirm}'); return false;">{$lang->myalerts_modal_delete_read}</a> | 
this line:
                    <a href="{$mybb->settings['bburl']}/alerts.php?action=mark_all_read&amp;my_post_key={$mybb->post_code}&amp;ret_link={$myalerts_return_link}">{$lang->myalerts_modal_mark_all_read}</a> | 

Also, prepend to this line in the template myalerts_page:
                            <a href="{$mybb->settings['bburl']}/alerts.php?action=delete_read&amp;my_post_key={$mybb->post_code}" onclick="return confirm('{$lang->myalerts_delete_read_confirm}')">{$lang->myalerts_page_delete_read}</a> | 
this line:
                            <a href="{$mybb->settings['bburl']}/alerts.php?action=mark_all_read&amp;my_post_key={$mybb->post_code}">{$lang->myalerts_page_mark_all_read}</a> | 

That's it!

Bonus challenge for any contenders: adapt the "Mark All Read" link in the modal pop-up to update the modal pop-up rather than - as currently is the case with these modifications - loading the full listing page.

Let me know of any problems or omissions. I am typing this all up after the fact, so I might have missed something.
(2022-09-06, 05:57 PM)Laird Wrote: [ -> ]Bonus challenge for any contenders: adapt the "Mark All Read" link in the modal pop-up to update the modal pop-up rather than - as currently is the case with these modifications - loading the full listing page.

A week later, and this unmet challenge was annoying me enough that I took it up myself. Here's how to replicate my solution:

In inc/plugins/myalerts.php, change this line (#1348 on GitHub) from:
	global $mybb, $lang, $templates, $db;
to:
	global $mybb, $lang, $templates, $db, $plugins;

In that same file, above this line (#1358 on GitHub):
	myalerts_create_instances();
insert this line:
	$plugins->run_hooks('global_start');
and below it, insert these lines:
	if ($mybb->get_input('action') == 'markAllRead') {
		$alertsList = MybbStuff_MyAlerts_AlertManager::getInstance()->getAlerts(0);

		$alertIds = array();

		if (!empty($alertsList) && is_array($alertsList)) {
			foreach ($alertsList as $alertObject) {
				$alert = parse_alert($alertObject);
				$alertIds[] = $alert['id'];
			}
		}

		MybbStuff_MyAlerts_AlertManager::getInstance()->markRead($alertIds);

		$mybb->input['from'] = 'header';
	}

Then, still in that same file, change the line immediately below (#1360 on GitHub) from:
	if ($mybb->get_input('action') == 'getNewAlerts') {
to:
	if (in_array($mybb->get_input('action'), array('getNewAlerts', 'markAllRead'))) {

Moving on to the file jscripts/myalerts.js, below this line (#8 on GitHub):
                deleteAlertProxy = $.proxy(this.deleteAlert, this),
add this line:
                markAllReadProxy = $.proxy(this.markAllRead, this),

In that same file, below this line (#14 on GitHub):
            bodySelector.on("click", ".deleteAlertButton", deleteAlertProxy);
add this line:
            bodySelector.on("click", ".markAllReadButton", markAllReadProxy);

Still in that same file, above this line (#30 on GitHub):
        module.prototype.getUnreadAlerts = function getUnreadAlerts(event) {
add these lines:
        module.prototype.markAllRead = function markAllRead(event) {
            event.preventDefault();
            $.get('xmlhttp.php?action=markAllRead', function (data) {
                $('#myalerts_alerts_modal tbody:first').html(data['template']);
                var msg = $('.alerts a').html();
                var idx = msg.indexOf(' (' + unreadAlerts + ')');
                if (idx > -1) {
                    msg = msg.substring(0, idx);
                    $('.alerts a').html(msg + ' (0)');
                }
                $('.alerts').removeClass('alerts--new');
            });       
        }

Finally, update your live myalerts_modal_content template via the ACP by editing the line added in the tutorial above from:
                    <a href="{$mybb->settings['bburl']}/alerts.php?action=mark_all_read&amp;my_post_key={$mybb->post_code}&amp;ret_link={$myalerts_return_link}">{$lang->myalerts_modal_mark_all_read}</a> | 
to:
                    <a class="markAllReadButton" href="{$mybb->settings['bburl']}/xmlhttp.php?action=markAllRead&amp;my_post_key={$mybb->post_code}&amp;ret_link={$myalerts_return_link}">{$lang->myalerts_modal_mark_all_read}</a> | 

Hopefully I haven't missed anything, and that works for you as-is.