1<?php 2 3use ComboStrap\AnalyticsDocument; 4use ComboStrap\ExceptionCombo; 5use ComboStrap\Identity; 6use ComboStrap\LogUtility; 7use ComboStrap\Message; 8use ComboStrap\Mime; 9use ComboStrap\Page; 10use ComboStrap\PluginUtility; 11use ComboStrap\QualityMenuItem; 12use ComboStrap\HttpResponse; 13 14if (!defined('DOKU_INC')) die(); 15if (!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 16 17 18require_once(__DIR__ . '/../ComboStrap/PluginUtility.php'); 19 20/** 21 * 22 * Show a quality message 23 * 24 * 25 * 26 */ 27class action_plugin_combo_qualitymessage extends DokuWiki_Action_Plugin 28{ 29 30 // a class can not start with a number 31 const QUALITY_BOX_CLASS = "quality-message"; 32 33 /** 34 * The quality rules that will not show 35 * up in the messages 36 */ 37 const CONF_EXCLUDED_QUALITY_RULES_FROM_DYNAMIC_MONITORING = "excludedQualityRulesFromDynamicMonitoring"; 38 /** 39 * Disable the message totally 40 */ 41 const CONF_DISABLE_QUALITY_MONITORING = "disableDynamicQualityMonitoring"; 42 43 const CANONICAL = "quality:dynamic_monitoring"; 44 45 const META_MANAGER_CALL_ID = "combo-quality-message"; 46 47 48 function __construct() 49 { 50 // enable direct access to language strings 51 // ie $this->lang 52 $this->setupLocale(); 53 } 54 55 public static function createHtmlQualityNote(Page $page): Message 56 { 57 if ($page->isSecondarySlot()) { 58 return Message::createErrorMessage("A has no quality metrics"); 59 60 } 61 62 63 if (!$page->exists()) { 64 return Message::createInfoMessage("The page does not exist"); 65 } 66 67 68 try { 69 $analyticsArray = $page->getAnalyticsDocument()->getJson()->toArray(); 70 } catch (ExceptionCombo $e) { 71 return Message::createErrorMessage("Error while trying to read the JSON analytics document. {$e->getMessage()}") 72 ->setStatus(HttpResponse::STATUS_INTERNAL_ERROR); 73 } 74 75 $rules = $analyticsArray[AnalyticsDocument::QUALITY][AnalyticsDocument::RULES]; 76 77 78 /** 79 * We may got null 80 * array_key_exists() expects parameter 2 to be array, 81 * null given in /opt/www/datacadamia.com/lib/plugins/combo/action/qualitymessage.php on line 113 82 */ 83 if ($rules == null) { 84 return Message::createInfoMessage("No rules found in the analytics document"); 85 } 86 87 /** 88 * If there is no info, nothing to show 89 */ 90 if (!array_key_exists(AnalyticsDocument::INFO, $rules)) { 91 return Message::createInfoMessage("No quality rules information to show"); 92 } 93 94 /** 95 * The error info 96 */ 97 $qualityInfoRules = $rules[AnalyticsDocument::INFO]; 98 99 /** 100 * Excluding the excluded rules 101 */ 102 global $conf; 103 $excludedRulesConf = $conf['plugin'][PluginUtility::PLUGIN_BASE_NAME][self::CONF_EXCLUDED_QUALITY_RULES_FROM_DYNAMIC_MONITORING]; 104 $excludedRules = preg_split("/,/", $excludedRulesConf); 105 foreach ($excludedRules as $excludedRule) { 106 if (array_key_exists($excludedRule, $qualityInfoRules)) { 107 unset($qualityInfoRules[$excludedRule]); 108 } 109 } 110 111 if (sizeof($qualityInfoRules) <= 0) { 112 return Message::createInfoMessage("No quality rules information to show"); 113 } 114 115 $qualityScore = $analyticsArray[AnalyticsDocument::QUALITY][renderer_plugin_combo_analytics::SCORING][renderer_plugin_combo_analytics::SCORE]; 116 $message = "<p>The page has a " . PluginUtility::getDocumentationHyperLink("quality:score", "quality score") . " of {$qualityScore}.</p>"; 117 118 $lowQuality = $analyticsArray[AnalyticsDocument::QUALITY][AnalyticsDocument::LOW]; 119 if ($lowQuality) { 120 121 $mandatoryFailedRules = $analyticsArray[AnalyticsDocument::QUALITY][AnalyticsDocument::FAILED_MANDATORY_RULES]; 122 $rulesUrl = PluginUtility::getDocumentationHyperLink("quality:rule", "rules"); 123 $lqPageUrl = PluginUtility::getDocumentationHyperLink("low_quality_page", "low quality page"); 124 $message .= "<div class='alert alert-warning'>This is a {$lqPageUrl} because it has failed the following mandatory {$rulesUrl}:"; 125 $message .= "<ul style='margin-bottom: 0'>"; 126 /** 127 * A low quality page should have 128 * failed mandatory rules 129 * but due to the asycn nature, sometimes 130 * we don't have an array 131 */ 132 if (is_array($mandatoryFailedRules)) { 133 foreach ($mandatoryFailedRules as $mandatoryFailedRule) { 134 $message .= "<li>" . PluginUtility::getDocumentationHyperLink("quality:rule#list", $mandatoryFailedRule) . "</li>"; 135 } 136 } 137 $message .= "</ul>"; 138 $message .= "</div>"; 139 } 140 $message .= "<p>You can still win a couple of points.</p>"; 141 $message .= "<ul>"; 142 foreach ($qualityInfoRules as $qualityRule => $qualityInfo) { 143 $message .= "<li>$qualityInfo</li>"; 144 } 145 $message .= "</ul>"; 146 147 if (!$page->isDynamicQualityMonitored()) { 148 $docLink = PluginUtility::getDocumentationHyperLink(":dynamic-quality-monitoring", "configuration"); 149 $message .= "<p>This page is not quality monitored due its $docLink.</p>"; 150 } 151 return Message::createInfoMessage($message); 152 153 } 154 155 156 function register(Doku_Event_Handler $controller) 157 { 158 159 160 /** 161 * Add a icon in the page tools menu 162 * https://www.dokuwiki.org/devel:event:menu_items_assembly 163 */ 164 $controller->register_hook('MENU_ITEMS_ASSEMBLY', 'AFTER', $this, 'addMenuItem'); 165 166 167 /** 168 * The ajax api to return data 169 */ 170 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'ajaxCall'); 171 172 } 173 174 175 function addMenuItem(Doku_Event $event, $param) 176 { 177 178 if (!Identity::isWriter()) { 179 return; 180 } 181 182 /** 183 * The `view` property defines the menu that is currently built 184 * https://www.dokuwiki.org/devel:menus 185 * If this is not the page menu, return 186 */ 187 if ($event->data['view'] != 'page') return; 188 189 global $INFO; 190 if (!$INFO['exists']) { 191 return; 192 } 193 array_splice($event->data['items'], -1, 0, array(new QualityMenuItem())); 194 195 } 196 197 /** 198 * Main function; dispatches the visual comment actions 199 * @param $event Doku_Event 200 */ 201 function ajaxCall(&$event, $param): void 202 { 203 $call = $event->data; 204 if ($call != self::META_MANAGER_CALL_ID) { 205 return; 206 } 207 //no other ajax call handlers needed 208 $event->stopPropagation(); 209 $event->preventDefault(); 210 211 /** 212 * Shared check between post and get HTTP method 213 */ 214 $id = $_GET["id"]; 215 if ($id === null) { 216 /** 217 * With {@link TestRequest} 218 * for instance 219 */ 220 $id = $_REQUEST["id"]; 221 } 222 223 if (empty($id)) { 224 HttpResponse::create(HttpResponse::STATUS_BAD_REQUEST) 225 ->setEvent($event) 226 ->setCanonical(self::CANONICAL) 227 ->send("The page id should not be empty", Mime::HTML); 228 return; 229 } 230 231 /** 232 * Quality is just for the writers 233 */ 234 if (!Identity::isWriter($id)) { 235 HttpResponse::create(HttpResponse::STATUS_NOT_AUTHORIZED) 236 ->setEvent($event) 237 ->send("Quality is only for writer", Mime::HTML); 238 return; 239 } 240 241 242 $page = Page::createPageFromId($id); 243 244 $message = self::createHtmlQualityNote($page); 245 246 $status = $message->getStatus(); 247 if ($status === null) { 248 $status = HttpResponse::STATUS_ALL_GOOD; 249 } 250 251 HttpResponse::create($status) 252 ->setEvent($event) 253 ->setCanonical(self::CANONICAL) 254 ->send($message->getContent(), Mime::HTML); 255 256 } 257} 258