CheckNotifications(); $this->ApplyFilters(); // Get active filters from the admin sessio if( !empty($gpAdmin['notification_filters']) ){ $this->filters = $gpAdmin['notification_filters']; }elseif( isset($gpAdmin['notifications']['filters']) ){ $this->filters = $gpAdmin['notifications']['filters']; unset($gpAdmin['notifications']); } } public static function GetSingleton(){ if( !self::$singleton ){ new self(); } } /** * Outputs a list of notifications * To be rendered in a gpabox * */ public function ListNotifications(){ global $langmessage; $this->FilterUserDefined(); $this->debug('$notifications = ' . pre($this->notifications)); echo '
'; $filter_list_by = ''; if( isset($_REQUEST['type']) ){ $filter_list_by = rawurldecode($_REQUEST['type']); $this->Tabs($filter_list_by); } foreach( $this->notifications as $type => $notification ){ if( empty($filter_list_by) ){ $title = $this->GetTitle($notification['title']); echo '

' . $title . '

'; }elseif( $type != $filter_list_by ){ continue; } echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; foreach( $notification['items'] as $id => $item ){ $tr_class = ''; $link_icon = ''; $link_title = $langmessage['Hide']; if( $item['priority'] < 0 ){ $tr_class = ' class="notification-item-muted"'; $link_icon = ''; $link_title = $langmessage['Show']; } echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
' . $langmessage['Item'] . '' . $langmessage['options'] . '' . $langmessage['Visibility'] . '
' . $item['label'] . '' . $item['action'] . ''; echo \gp\tool::Link( 'Admin/Notifications/Manage', $link_icon, 'cmd=toggle_priority' . '&id=' . rawurlencode($id) . '&type=' . rawurlencode($filter_list_by), array( 'title' => $link_title, 'class' => 'toggle-notification', 'data-cmd' => 'gpabox', ) ); echo '
'; } echo '

'; echo ''; echo '

'; echo '
'; } /** * Tabs * Output tabs to navigate between notifications * @param string $filter_list_by tab type to be active * */ public function Tabs($filter_list_by){ echo '
'; foreach( $this->notifications as $type => $notification ){ $class = ''; if( $filter_list_by && $type == $filter_list_by ){ $class = 'selected'; } echo \gp\tool::Link( 'Admin/Notifications', $this->GetTitle($notification['title']) . ' ('.$notification['count'].')', 'cmd=ShowNotifications&type=' . rawurlencode($type), array( 'title' => $this->GetTitle($notification['title']), 'class' => $class, 'style' => 'margin-right:0.5em;', 'data-cmd' => 'gpabox', ) ); } echo '
'; } /** * Notification Title * @param string $title * @return string $title translation if available * */ public function GetTitle($title){ global $langmessage; if( isset($langmessage[$title]) ){ return $langmessage[$title]; } return htmlspecialchars($title); } /** * Manage Notifications * Set display filters and priority for notification items by $_REQUEST * */ public function ManageNotifications(){ global $gpAdmin; $cmd = \gp\tool::GetCommand(); switch( $cmd ){ case 'toggle_priority': if( !empty($_REQUEST['id']) ){ $this->SetFilter($_REQUEST['id'], 'toggle_priority'); } break; case 'set_priority': if( !empty($_REQUEST['id']) && isset($_REQUEST['new_priority']) && is_numeric($_REQUEST['new_priority']) ){ $this->SetFilter($_REQUEST['id'], 'set_priority', $_REQUEST['new_priority']); } break; } $gpAdmin['notification_filters'] = array_filter($this->filters); // Save filters to the admin session $this->ListNotifications(); self::UpdateNotifications(); } /** * Set a single filter * @param string $id of notification item to be modified * @param string $do filter action * @param string $val filter value * * currently implemented actions: * 'toggle_priority' will toggle priority between -1 (=muted) and it's initial value * 'set_priority' will set the priority value to $val * */ public function SetFilter($id, $do, $val=false){ // check if id exists in notifications $id_exists = false; foreach( $this->notifications as $type => $notification ){ if( array_key_exists($id,$notification['items']) ){ $id_exists = true; break; } } if( !$id_exists ){ // notification id no longer exists, purge possible stray filter if( isset($this->filters[$id]) ){ unset($this->filters[$id]); } return; } switch( $do ){ case 'toggle_priority': if( isset($this->filters[$id]['priority']) ){ unset($this->filters[$id]['priority']); }else{ $this->filters[$id]['priority'] = -1; } break; case 'set_priority': $this->filters[$id]['priority'] = (int)$val; return; } $this->debug( 'Notifications SetFilter Error: unknown command "' . htmlspecialchars($do) . '"' ); } /** * Apply filters to the notifications array * Remove inapropriate items for users lacking permissions to deal with them * Apply user defined (display) filters * */ public function ApplyFilters(){ global $gpAdmin; // Remove items lacking user permissions and therefore cannot be dealt with anyway // deprecated addons if( !\gp\admin\Tools::HasPermission('Admin/Addons') ){ $this->FilterType('Deprecated Addons','deprecated_addons'); } // debug / development if( $gpAdmin['granted'] != 'all' || $gpAdmin['editing'] != 'all' ){ $this->FilterType('Development','superuser'); } // extra content draft if( !\gp\admin\Tools::HasPermission('Admin/Extra') ){ $this->FilterType('Working Drafts','extra'); } // theme update if( !\gp\admin\Tools::HasPermission('Admin_Theme_Content/Remote') ){ $this->FilterType('updates','theme'); } // addon update if( !\gp\admin\Tools::HasPermission('Admin/Addons/Remote') ){ $this->FilterType('updates','plugin'); } // core update if( !\gp\admin\Tools::HasPermission('Admin/Uninstall') ){ // can't find a permission for core updates so I use Uninstall $this->FilterType('updates','core'); } // page draft $this->FilterCallback('Working Drafts',function($item){ if( $item['type'] == 'page' && !\gp\admin\Tools::CanEdit($item['title']) ){ return true; } return false; }); // private page $this->FilterCallback('Private Pages',function($item){ if( !\gp\admin\Tools::CanEdit($item['title']) ){ return true; } return false; }); } /** * Apply user defined (display) filters * Remove empty, count items, and get priority * */ public function FilterUserDefined(){ foreach( $this->notifications as $notification_type => &$notification ){ if( empty($this->notifications[$notification_type]['items']) ){ unset($this->notifications[$notification_type]); continue; } $count = 0; $priority = 0; $total_priority = 0; foreach( $notification['items'] as $id => &$item ){ $total_priority = max( $item['priority'], $total_priority ); if( isset($this->filters[$id]) ){ $item = $this->filters[$id] + $item; } if( $item['priority'] > 0 ){ $priority = max( $item['priority'], $priority ); $count++; } } $notification['count'] = $count; $notification['priority'] = $priority; $notification['total_priority'] = $total_priority; } // sort by priority uasort($this->notifications,function($a,$b){ if( $b['priority'] !== $a['priority'] ){ return strnatcmp($b['priority'],$a['priority']); } if( $b['total_priority'] !== $a['total_priority'] ){ return strnatcmp($b['total_priority'],$a['total_priority']); } return strnatcmp($b['title'],$a['title']); }); } /** * Filter notifications matching a notification type and item type * */ public function FilterType( $notification_type, $item_type ){ if( !isset($this->notifications[$notification_type]) ){ return; } foreach( $this->notifications[$notification_type]['items'] as $id => $item ){ if( $item['type'] !== $item_type ){ continue; } unset($this->notifications[$notification_type]['items'][$id]); } } /** * Filter notifications matching a notification type with a callback * */ public function FilterCallback( $notification_type, $callback ){ if( !isset($this->notifications[$notification_type]) ){ return; } foreach( $this->notifications[$notification_type]['items'] as $id => $item ){ if( $callback($item) === true ){ unset($this->notifications[$notification_type]['items'][$id]); } } } /** * Aggregate all sources of notifications * */ public function CheckNotifications(){ $this->notifications = array(); $items = $this->GetDrafts(); $this->Add('Working Drafts', $items, '#329880'); $items = $this->GetPrivatePages(); $this->Add('Private Pages', $items, '#ad5f45'); $items = $this->GetUpdatesNotifications(); $this->Add('updates', $items, '#3153b7'); $items = $this->GetDeprecatedAddons(); $this->Add('Deprecated Addons', $items, '#d61b1b'); $items = $this->GetDebugNotifications(); $this->Add('Development', $items, '#ff8c00', '#000'); \gp\tool\Plugins::Action('Notifications',[$this]); } /** * Add Notifications * @param string $title will sho up as panelgroup submenu item * @param array $items see below * @param string $bg (optional) badge background color (any valid css color value) * @param string $color (optional) badge text color (any valid color value), use a dark color on light $bg * * $items are associative arrays containing the following keys: * 'label' => (string) text or html, first element of the item row * 'id' => (string) arbitrary but permanently unique string or number (required for user defined filtering) * 'priority' => (integer) a number above 0, determines the ranking amongst other notifications. Anything above 100 is high-priority * 'action' => (string) plain text or html describing and/or linking to possible solutions * 'type' => (string) optional, only relevant for automatic system-internal filters * */ public function Add( $title, $items, $bg = '#555', $color = '#fff'){ if( empty($items) ){ return; } if( !isset($this->notifications[$title]) ){ $this->notifications[$title] = [ 'title' => $title, 'badge_bg' => $bg, 'badge_color' => $color, 'items' => [], ]; } foreach( $items as $item ){ if( !isset($item['id']) ){ trigger_error('id not set for notification '.pre($item)); // should we create an id? continue; } $item += ['priority'=>0]; $item['priority'] = (int)$item['priority']; $id = hash('crc32b', $item['id']); unset($item['id']); $this->notifications[$title]['items'][$id] = $item; } } /** * Get Notifications * Outputs a Notifications panelgroup * @param boolean $in_panel if panelgroup shall be rendered in admin menu * */ public function GetNotifications($in_panel=true){ global $langmessage; $this->FilterUserDefined(); if( count($this->notifications) < 1 ){ return; } $total_count = 0; $main_badge_style = ''; $expand_class = ''; // expand_child_click $badge_format = ' (%2$d)'; $panel_class = ''; $default_style = ['badge_bg'=>'transparent','color'=>'#fff']; if( $in_panel ){ $badge_format = ' %2$d'; $expand_class = 'expand_child'; $panel_class = 'admin-panel-notifications'; } ob_start(); foreach($this->notifications as $type => $notification ){ if( empty($notification['items']) ){ $this->debug('notification => items subarray mising or empty'); continue; } $total_count += $notification['count']; $title = $this->GetTitle($notification['title']); $badge_html = ''; $badge_style = ''; $notification += $default_style; if( $notification['count'] > 0 ){ $badge_style = 'background-color:'.$notification['badge_bg'].';color:'.$notification['badge_color'].';'; $badge_html = sprintf($badge_format, $badge_style, $notification['count']); } echo '
  • '; echo \gp\tool::Link( 'Admin/Notifications', $title . $badge_html, 'cmd=ShowNotifications&type=' . rawurlencode($type), array( 'title' => $notification['count'] . ' ' . $title, 'class' => 'admin-panel-notification', // . '-' . rawurlencode($type)', 'data-cmd' => 'gpabox', ) ); echo '
  • '; if( empty($main_badge_style) ){ $main_badge_style = $badge_style; } } $links = ob_get_clean(); $panel_label = $langmessage['Notifications']; $badge_html = $total_count > 0 ? '' . $total_count . '' : ''; \gp\Admin\Tools::_AdminPanelLinks( $in_panel, $links, $panel_label, 'fa fa-bell', 'notifications', $panel_class, // new param 'class' $badge_html // new param 'badge' ); } /** * Get a Notification array for deprecatd addons as defined in /gpconfig.php * @return array $deprecated_note single notification array * */ public function GetDeprecatedAddons(){ global $config, $deprecated_addons, $langmessage; $deprecated_note = array(); if( !\notify_deprecated || !is_array($config['addons']) || empty($deprecated_addons) ){ return $deprecated_note; } foreach($deprecated_addons as $deprec_addon_name => $deprec_addon_data){ if( empty($deprec_addon_data['notify']) ){ continue; } foreach( $config['addons'] as $inst_addon_key => $inst_addon_data ){ if( $deprec_addon_name != $inst_addon_data['name'] ){ // addon is not installed continue; } $version_affected = empty($deprec_addon_data['upto_version']) || $deprec_addon_data['upto_version'] == 'all' || version_compare($deprec_addon_data['upto_version'], $inst_addon_data['version'], '<='); if( !$version_affected ){ continue; } $label = $deprec_addon_name; $uninstall_link = \gp\tool::Link( 'Admin/Addons', $langmessage['uninstall'], 'cmd=uninstall&addon=' . $inst_addon_data['data_folder'], array( 'data-cmd' => 'gpabox', 'title' => $langmessage['uninstall'] ) ); $action = empty($deprec_addon_data['reason']) ? $uninstall_link : $deprec_addon_data['reason'] . ' ' . $uninstall_link; $deprecated_note[] = array( 'type' => 'deprecated_addons', 'label' => $label, 'id' => 'deprecated addon ' . $label, 'priority' => 200, // that's a rather high priority 'action' => $action, ); } } return $deprecated_note; } /** * Get a Notification array for debugging / development relevant information * @return array $debug_note single notification array * */ public function GetDebugNotifications(){ global $langmessage; $debug_note = array(); if( ini_get('display_errors') ){ $label = 'ini_set(display_errors,' . htmlspecialchars(ini_get('display_errors')) . ')'; $debug_note[] = array( 'type' => 'any_user', 'label' => $label, // Adding the server name to the id makes sure that it will change when moving the site (e.g. when going live) // Thus possible set hide-filters will invalidate and the warning will show up again. 'id' => $label . \gp\tool::ServerName(), 'priority' => 500, // that's a high priority 'action' => 'edit gpconfig.php or notify administrator!
    This should only be enabled in exceptional cases.', ); } if( \gpdebug ){ $label = 'gpdebug is enabled'; $debug_note[] = array( 'type' => 'superuser', 'label' => $label, 'id' => $label . \gp\tool::ServerName(), 'priority' => 75, 'action' => 'edit gpconfig.php', ); } if( \create_css_sourcemaps ){ $label = 'create_css_sourcemaps is enabled'; $debug_note[] = array( 'type' => 'superuser', 'label' => $label, 'id' => $label . \gp\tool::ServerName(), 'priority' => 75, 'action' => 'edit gpconfig.php', ); } return $debug_note; } /** * Convert $new_versions to notification array * @return array $updated single notification array containing possible updates * */ public function GetUpdatesNotifications(){ global $langmessage; $updates = array(); if( gp_remote_update && isset(\gp\Admin\Tools::$new_versions['core']) ){ $label = \CMS_NAME . ' ' . \gp\Admin\Tools::$new_versions['core']; $updates[] = array( 'type' => 'core', 'label' => $label, 'id' => $label, 'priority' => 60, 'action' => '' . $langmessage['upgrade'] . '', ); } foreach(\gp\Admin\Tools::$new_versions as $addon_id => $new_addon_info){ if( !is_numeric($addon_id) ){ continue; } $label = $new_addon_info['name'] . ': ' . $new_addon_info['version']; $url = \gp\admin\Tools::RemoteUrl( $new_addon_info['type'] ); if( $url === false ){ continue; } $updates[] = array( 'type' => $new_addon_info['type'], 'label' => $label, 'id' => $label, 'priority' => 60, 'action' => '' . $langmessage['upgrade'] . '', ); } return $updates; } /** * Update the Notifications panelgroup in Admin Menu via AJAX * */ public static function UpdateNotifications(){ global $page; if( !\gp\admin\Tools::HasPermission('Admin/Notifications') ){ return; } self::GetSingleton(); ob_start(); self::$singleton->GetNotifications(); $panelgroup = ob_get_clean(); $page->ajaxReplace[] = array('replace', '.admin-panel-notifications', $panelgroup); } /** * Get information about working drafts * @return array $drafts single notification array of current working drafts * */ public function GetDrafts(){ global $dataDir, $gp_index, $gp_titles, $langmessage; $draft_types = array( 'page' => $dataDir . '/data/_pages', 'extra' => $dataDir . '/data/_extra', ); $drafts = array(); foreach( $draft_types as $type => $dir ){ $folders = \gp\tool\Files::readDir($dir,1); foreach( $folders as $folder ){ // no draft $draft_path = $dir . '/' . $folder . '/draft.php'; if( !\gp\tool\Files::Exists($draft_path) ){ continue; } // drafts of 'trashed' pages will not count $deleted_path = $dir . '/' . $folder . '/deleted.php'; if( \gp\tool\Files::Exists($deleted_path) ){ continue; } $draft = array( 'type' => $type, 'id' => $folder, 'priority' => 100, ); switch( $type ){ case 'extra': $draft['label'] = str_replace('_', ' ', $folder) . ' (' . $langmessage['theme_content'] . ')'; $draft['action'] = \gp\tool::Link( 'Admin/Extra', $langmessage['theme_content'], '', array( 'class' => 'getdrafts-extra-content-link', 'title' => $langmessage['theme_content'], ) ); $draft['preview_link'] = \gp\tool::Link( 'Admin/Extra', $langmessage['preview'], 'cmd=PreviewText&file=' . $folder, array( 'class' => 'getdrafts-extra-preview', 'title' => $langmessage['preview'], ) ); $draft['publish_link'] = \gp\tool::Link( 'Admin/Extra', $langmessage['Publish Draft'], 'cmd=PublishDraft&file=' . $folder, array( 'data-cmd' => 'gpajax', 'class' => 'getdrafts-extra-publish', 'title' => $langmessage['Publish Draft'], ) ); $draft['folder'] = $folder; break; case 'page': $draft['index'] = substr($folder, strpos($folder, "_") + 1); $draft['title'] = \gp\tool::IndexToTitle($draft['index']); $draft['label'] = \gp\tool::GetLabel($draft['title']) . ' (' . $langmessage['Page'] . ')'; $draft['action'] = \gp\tool::Link( $draft['title'], $langmessage['view/edit_page'], //$draft['label'], '', array( 'class' => 'getdrafts-page-link', 'title' => $langmessage['view/edit_page'], ) ); $draft['publish_link'] = \gp\tool::Link( $draft['title'], $langmessage['Publish Draft'], 'cmd=PublishDraft', array( 'data-cmd' => 'creq', 'class' => 'getdrafts-page-publish', 'title' => $langmessage['Publish Draft'], ) ); break; } $drafts[] = $draft; } } return $drafts; } /** * Get information of all private (invisible) pages * @return array $private_pages single notification array of pages currently set as private * */ public function GetPrivatePages(){ global $gp_titles, $langmessage, $page; $private_pages = array(); foreach( $gp_titles as $index => $title ){ if( !isset($title['vis']) || $title['vis'] !== 'private' ){ continue; } $private_page = array( 'index' => $index, 'title' => \gp\tool::IndexToTitle($index), 'id' => 'private_page' . $index, 'priority' => 40, 'label' => \gp\tool::GetLabelIndex($index), ); // increase priority by 100 when viewing the current page if( isset($page->gp_index) && $page->gp_index == $index ){ $private_page['priority'] += 100; } $private_page['action'] = \gp\tool::Link( $private_page['title'], $langmessage['view/edit_page'], '', array( 'class' => 'getprivate-page-link', 'title' => $langmessage['view/edit_page'], ) ); $private_page['make_public_link'] = \gp\tool::Link( 'Admin/Menu/Ajax', $langmessage['Visibility'] . ' ' . $langmessage['Public'], 'cmd=ToggleVisibility&index=' . $index, array( 'data-cmd' => 'postlink', 'class' => 'getprivate-make-public', 'title' => $langmessage['Publish Draft'], ) ); $private_pages[] = $private_page; } return $private_pages; } public function debug($msg){ $this->debug && debug($msg); } } }