1<?php 2 3namespace dokuwiki\plugin\extension; 4 5class Notice 6{ 7 public const INFO = 'info'; 8 public const WARNING = 'warning'; 9 public const ERROR = 'error'; 10 public const SECURITY = 'security'; 11 12 protected const ICONS = [ 13 self::INFO => 'I', 14 self::WARNING => 'W', 15 self::ERROR => 'E', 16 self::SECURITY => 'S', 17 ]; 18 19 protected $notices = [ 20 self::INFO => [], 21 self::WARNING => [], 22 self::ERROR => [], 23 self::SECURITY => [], 24 ]; 25 26 /** @var \helper_plugin_extension */ 27 protected $helper; 28 29 /** @var Extension */ 30 protected Extension $extension; 31 32 /** 33 * Not public, use list() instead 34 * @param Extension $extension 35 */ 36 protected function __construct(Extension $extension) 37 { 38 $this->helper = plugin_load('helper', 'extension'); 39 $this->extension = $extension; 40 41 $this->checkSecurity(); 42 $this->checkURLChange(); 43 $this->checkFolder(); 44 $this->checkPHPVersion(); 45 $this->checkDependencies(); 46 $this->checkConflicts(); 47 $this->checkUpdateMessage(); 48 $this->checkPermissions(); 49 $this->checkUnusedAuth(); 50 $this->checkGit(); 51 } 52 53 /** 54 * Get all notices for the extension 55 * 56 * @return string[][] array of notices grouped by type 57 */ 58 public static function list(Extension $extension): array 59 { 60 $self = new self($extension); 61 return $self->notices; 62 } 63 64 /** 65 * Return the icon path for a notice type 66 * 67 * @param string $type The notice type constant 68 * @return string 69 */ 70 public static function icon($type): string 71 { 72 if (!isset(self::ICONS[$type])) throw new \RuntimeException('Unknown notice type: ' . $type); 73 return __DIR__ . '/images/' . $type . '.svg'; 74 } 75 76 /** 77 * Return the character symbol for a notice type used on CLI 78 * 79 * @param string $type The notice type constant 80 * @return string 81 */ 82 public static function symbol($type): string 83 { 84 if (!isset(self::ICONS[$type])) throw new \RuntimeException('Unknown notice type: ' . $type); 85 return self::ICONS[$type][0] ?? ''; 86 } 87 88 /** 89 * Access a language string 90 * 91 * @param string $msg 92 * @return string 93 */ 94 protected function getLang($msg) 95 { 96 return $this->helper->getLang($msg); 97 } 98 99 /** 100 * Check that all dependencies are met 101 * @return void 102 */ 103 protected function checkDependencies() 104 { 105 if (!$this->extension->isInstalled()) return; 106 107 $dependencies = $this->extension->getDependencyList(); 108 $missing = []; 109 foreach ($dependencies as $dependency) { 110 try { 111 $dep = Extension::createFromId($dependency); 112 } catch (\RuntimeException) { 113 // ignore malformed dependency ids in the repository metadata 114 continue; 115 } 116 if (!$dep->isInstalled()) $missing[] = $dep; 117 } 118 if (!$missing) return; 119 120 $this->notices[self::ERROR][] = sprintf( 121 $this->getLang('missing_dependency'), 122 implode(', ', array_map(static fn(Extension $dep) => $dep->getId(true), $missing)) 123 ); 124 } 125 126 /** 127 * Check if installed dependencies are conflicting 128 * @return void 129 */ 130 protected function checkConflicts() 131 { 132 $conflicts = $this->extension->getConflictList(); 133 $found = []; 134 foreach ($conflicts as $conflict) { 135 try { 136 $dep = Extension::createFromId($conflict); 137 } catch (\RuntimeException) { 138 // ignore malformed conflict ids in the repository metadata 139 continue; 140 } 141 if ($dep->isInstalled()) $found[] = $dep; 142 } 143 if (!$found) return; 144 145 $this->notices[self::WARNING][] = sprintf( 146 $this->getLang('found_conflict'), 147 implode(', ', array_map(static fn(Extension $dep) => $dep->getId(true), $found)) 148 ); 149 } 150 151 /** 152 * Check for security issues 153 * @return void 154 */ 155 protected function checkSecurity() 156 { 157 if ($issue = $this->extension->getSecurityIssue()) { 158 $this->notices[self::SECURITY][] = sprintf($this->getLang('security_issue'), $issue); 159 } 160 if ($issue = $this->extension->getSecurityWarning()) { 161 $this->notices[self::SECURITY][] = sprintf($this->getLang('security_warning'), $issue); 162 } 163 } 164 165 /** 166 * Check if the extension is installed in correct folder 167 * @return void 168 */ 169 protected function checkFolder() 170 { 171 if (!$this->extension->isInWrongFolder()) return; 172 173 $this->notices[self::ERROR][] = sprintf( 174 $this->getLang('wrong_folder'), 175 basename($this->extension->getCurrentDir()), 176 basename($this->extension->getInstallDir()) 177 ); 178 } 179 180 /** 181 * Check PHP requirements 182 * @return void 183 */ 184 protected function checkPHPVersion() 185 { 186 try { 187 Installer::ensurePhpCompatibility($this->extension); 188 } catch (\Exception $e) { 189 $this->notices[self::ERROR][] = $e->getMessage(); 190 } 191 } 192 193 /** 194 * Check for update message 195 * @return void 196 */ 197 protected function checkUpdateMessage() 198 { 199 // only display this for installed extensions 200 if (!$this->extension->isInstalled()) return; 201 if ($msg = $this->extension->getUpdateMessage()) { 202 $this->notices[self::WARNING][] = sprintf($this->getLang('update_message'), $msg); 203 } 204 } 205 206 /** 207 * Check for URL changes 208 * @return void 209 */ 210 protected function checkURLChange() 211 { 212 if (!$this->extension->hasChangedURL()) return; 213 $this->notices[self::WARNING][] = sprintf( 214 $this->getLang('url_change'), 215 $this->extension->getDownloadURL(), 216 $this->extension->getManager()->getDownloadURL() 217 ); 218 } 219 220 /** 221 * Check if the extension dir has the correct permissions to change 222 * 223 * @return void 224 */ 225 protected function checkPermissions() 226 { 227 try { 228 Installer::ensurePermissions($this->extension); 229 } catch (\Exception $e) { 230 $this->notices[self::ERROR][] = $e->getMessage(); 231 } 232 } 233 234 /** 235 * Hint about unused auth plugins 236 * 237 * @return void 238 */ 239 protected function checkUnusedAuth() 240 { 241 global $conf; 242 if ( 243 $this->extension->isEnabled() && 244 in_array('Auth', $this->extension->getComponentTypes()) && 245 $conf['authtype'] != $this->extension->getID() 246 ) { 247 $this->notices[self::INFO][] = $this->getLang('auth'); 248 } 249 } 250 251 /** 252 * Hint about installations by git 253 * 254 * @return void 255 */ 256 protected function checkGit() 257 { 258 if ($this->extension->isGitControlled()) { 259 $this->notices[self::INFO][] = $this->getLang('git'); 260 } 261 } 262} 263