PageRenderTime 36ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/public_html/wire/core/MarkupQA.php

https://bitbucket.org/thomas1151/mats
PHP | 723 lines | 474 code | 72 blank | 177 comment | 102 complexity | 2cadb4853edd35b04c473510d4339640 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php namespace ProcessWire;
  2. /**
  3. * HTML Markup Quality Assurance
  4. *
  5. * Provides runtime quality assurance for markup stored in [textarea] field values.
  6. *
  7. * 1. Ensures URLs referenced in <a> and <img> tags are relative to actual site root.
  8. * 2. Ensures local page URLs referenced in <a> tags up-to-date with current $page URL.
  9. * 3. Identifies and logs <img> tags that point to non-existing files in PW's file system.
  10. * 4. Re-creates image variations that don't exist, when the original still exists.
  11. * 5. Populates blank 'alt' attributes with actual file description.
  12. *
  13. * - For #1 use the wakeupUrls($value) and sleepUrls($value) methods.
  14. * - For #2 use the wakeupHrefs($value) and sleepHrefs($value) methods.
  15. * - For #3-5 use the checkImgTags($value, $options) method, where $options specifies 3-5.
  16. *
  17. * Runtime errors are logged to: /site/assets/logs/markup-qa-errors.txt
  18. *
  19. * ProcessWire 3.x, Copyright 2016 by Ryan Cramer
  20. * https://processwire.com
  21. *
  22. */
  23. class MarkupQA extends Wire {
  24. const errorLogName = 'markup-qa-errors';
  25. /**
  26. * @var string
  27. *
  28. */
  29. protected $assetsURL = '';
  30. /**
  31. * @var Page
  32. *
  33. */
  34. protected $page;
  35. /**
  36. * @var Field
  37. *
  38. */
  39. protected $field;
  40. /**
  41. * Whether or not to track verbose info to $page
  42. *
  43. * $page->_markupQA = array('field_name' => array(counts)))
  44. *
  45. * @var bool
  46. *
  47. */
  48. protected $verbose = false;
  49. /**
  50. * Whether verbose debug mode is active
  51. *
  52. * @var bool
  53. *
  54. */
  55. protected $debug = false;
  56. /**
  57. * Construct
  58. *
  59. * @param Page $page
  60. * @param Field $field
  61. *
  62. */
  63. public function __construct(Page $page = null, Field $field = null) {
  64. if($page) $this->setPage($page);
  65. if($field) $this->setField($field);
  66. $this->assetsURL = $this->wire('config')->urls->assets;
  67. if($this->wire('config')->debugMarkupQA) {
  68. $user = $this->wire('user');
  69. if($user) $this->debug = $user->isSuperuser();
  70. }
  71. }
  72. /**
  73. * Enable or disable verbose mode
  74. *
  75. * @param bool $verbose
  76. *
  77. */
  78. public function setVerbose($verbose) {
  79. $this->verbose = $verbose ? true : false;
  80. }
  81. /**
  82. * Set the current Page
  83. *
  84. * @param Page $page
  85. *
  86. */
  87. public function setPage(Page $page) {
  88. $this->page = $page;
  89. }
  90. /**
  91. * Set the current Field
  92. *
  93. * @param Field $field
  94. *
  95. */
  96. public function setField(Field $field) {
  97. $this->field = $field;
  98. }
  99. /**
  100. * Wakeup URLs in href or src attributes for presentation
  101. *
  102. * @param $value
  103. *
  104. */
  105. public function wakeupUrls(&$value) {
  106. $this->checkUrls($value, false);
  107. }
  108. /**
  109. * Sleep URLs in href or src attributes for storage
  110. *
  111. * @param $value
  112. *
  113. */
  114. public function sleepUrls(&$value) {
  115. $this->checkUrls($value, true);
  116. }
  117. /**
  118. * Wake URLs for wakeup or sleep, converting root URLs as necessary
  119. *
  120. * @param string $value
  121. * @param bool $sleep
  122. *
  123. */
  124. protected function checkUrls(&$value, $sleep = false) {
  125. // see if quick exit possible
  126. if(stripos($value, 'href=') === false && stripos($value, 'src=') === false) return;
  127. $config = $this->wire('config');
  128. $httpHost = $config->httpHost;
  129. $rootURL = $config->urls->root;
  130. $rootHostURL = $httpHost . $rootURL;
  131. $replacements = array(
  132. // wakeup => sleep
  133. " href=\"$rootURL" => "\thref=\"/",
  134. " href='$rootURL" => "\thref='/",
  135. " src=\"$rootURL" => "\tsrc=\"/",
  136. " src='$rootURL" => "\tsrc='/",
  137. );
  138. if(strpos($value, "//$rootHostURL")) $replacements = array_merge($replacements, array(
  139. // wakeup => sleep
  140. " href='http://$rootHostURL" => "\thref='http://$httpHost/",
  141. " href='https://$rootHostURL" => "\thref='https://$httpHost/",
  142. " href=\"http://$rootHostURL" => "\thref=\"http://$httpHost/",
  143. " href=\"https://$rootHostURL" => "\thref=\"https://$httpHost/",
  144. ));
  145. if($sleep) {
  146. // sleep
  147. $value = str_ireplace(array_keys($replacements), array_values($replacements), $value);
  148. if($this->verbose && $this->page && $this->field) {
  149. $info = $this->page->get('_markupQA');
  150. if(!is_array($info)) $info = array();
  151. if(!is_array($info[$this->field->name])) $info[$this->field->name] = array();
  152. $info[$this->field->name]['href'] = substr_count($value, "\thref=");
  153. $info[$this->field->name]['src'] = substr_count($value, "\tsrc=");
  154. $this->page->setQuietly('_markupQA', $info);
  155. }
  156. } else if(strpos($value, "\t") === false) {
  157. // wakeup, but nothing necessary (quick exit)
  158. return;
  159. } else {
  160. // wakeup
  161. $value = str_ireplace(array_values($replacements), array_keys($replacements), $value);
  162. }
  163. }
  164. /**
  165. * Convert a relative path to be absolute
  166. *
  167. * @param string $path
  168. * @return string Returns absolute path, or blank string on error
  169. *
  170. */
  171. protected function relativeToAbsolutePath($path) {
  172. // path is relative, i.e. "something/" or "./something/ or "../something/" or similar
  173. $_path = $path;
  174. $page = $this->page;
  175. $slashUrls = $page->template->slashUrls;
  176. if(strpos($path, './') === 0) {
  177. // remove leading "./" reference, making "./something/" => "something/"
  178. $path = substr($path, 2);
  179. }
  180. if(strpos($path, '.') !== 0 && !$slashUrls) {
  181. // path like "something/"
  182. // if slashUrls are not in use, then the meaning of "./" is parent rather than page
  183. $page = $page->parent();
  184. $slashUrls = $page->template->slashUrls;
  185. }
  186. // resolve leading "../" to a $page
  187. while(strpos($path, '../') === 0) {
  188. $page = $slashUrls ? $page->parent() : $page->parent()->parent();
  189. $path = substr($path, 3);
  190. $slashUrls = $page->template->slashUrls;
  191. if(!$page->id) break;
  192. }
  193. if(!$page->id) {
  194. // path resolved outside of PW's tree
  195. $path = '';
  196. } else if(strlen($path)) {
  197. // resolve path from $page plus remaining path
  198. $path = rtrim($page->path, '/') . '/' . ltrim($path, '/');
  199. } else {
  200. // resolve path from $page
  201. $path = $page->path;
  202. }
  203. if($path && $path != $_path) {
  204. if($this->debug) $this->message("MarkupQA absoluteToRelative converted: $_path => $path");
  205. }
  206. return $path;
  207. }
  208. /**
  209. * Sleep href attributes, adding a data-pwid attribute to <a> tags that resolve to a Page
  210. *
  211. * Should be used AFTER sleepUrls() has already been called, so that any URLs are already
  212. * relative to "/" rather than potential "/subdir/".
  213. *
  214. * @param string $value
  215. *
  216. */
  217. public function sleepLinks(&$value) {
  218. // if there are no href attributes, there's nothing to sleep
  219. // if there is already a data-pwid attribute present, then links are already asleep
  220. if(strpos($value, 'href=') === false || strpos($value, 'data-pwid=')) return;
  221. $info = $this->verbose ? $this->page->get('_markupQA') : array();
  222. if(!is_array($info)) $info = array();
  223. if(isset($info[$this->field->name])) {
  224. $counts = $info[$this->field->name];
  225. } else {
  226. $counts = array(
  227. 'external' => 0,
  228. 'internal' => 0,
  229. 'relative' => 0,
  230. 'files' => 0,
  231. 'other' => 0,
  232. 'unresolved' => 0,
  233. 'nohttp' => 0,
  234. );
  235. }
  236. $re = '!' .
  237. '(<a[^\t<>]*?)' . // 1:"start" which includes the starting <a tag and everything before href attribute
  238. '([\t ]+href=(?:["\'](?:https?:)?//[^/"\'\s<>]+|["\']))' . // 2:"href" with optional hostname
  239. '([-_./a-z0-9]*)' . // 3:"path" in ProcessWire page name format
  240. '([^<>]*>)' . // 4:"end" which includes everything after the path (potential query string, other attrs, etc.)
  241. '!i';
  242. if(!preg_match_all($re, $value, $matches)) return;
  243. $replacements = array();
  244. $languages = $this->wire('languages');
  245. if($languages && !$this->wire('modules')->isInstalled('LanguageSupportPageNames')) $languages = null;
  246. foreach($matches[3] as $key => $path) {
  247. if(!strlen($path)) continue;
  248. $full = $matches[0][$key];
  249. $start = $matches[1][$key];
  250. $href = $matches[2][$key];
  251. $end = $matches[4][$key];
  252. $_path = $path; // original unmodified path
  253. if(strpos($href, '//')) {
  254. // scheme and hostname present
  255. /** @noinspection PhpUnusedLocalVariableInspection */
  256. list($x, $host) = explode('//', $href);
  257. if($host != $this->wire('config')->httpHost && !in_array($host, $this->wire('config')->httpHosts)) {
  258. $counts['external']++;
  259. if($this->debug) $this->message("MarkupQA sleepLinks skipping because hostname: $host");
  260. // external hostname, which we will skip over
  261. continue;
  262. }
  263. } else if(strpos($href, ':') !== false || strpos($end, ':') === 0) {
  264. // non http link like mailto: or tel:
  265. $counts['nohttp']++;
  266. continue;
  267. }
  268. if(strpos($path, '/') !== 0) {
  269. // convert relative path to absolute
  270. $path = $this->relativeToAbsolutePath($path);
  271. if(!strlen($path)) continue;
  272. if($path != $_path) $counts['relative']++;
  273. } else if(strrpos($path, '.') > strrpos($path, '/')) {
  274. // not relative and possibly a filename
  275. // if this link is to a file that exists, then it's not a page link so skip it
  276. $file = $this->wire('config')->paths->root . ltrim($path, '/');
  277. if(file_exists($file)) {
  278. $counts['files']++;
  279. continue;
  280. }
  281. }
  282. $pageID = $this->wire('pages')->getByPath($path, array(
  283. 'getID' => true,
  284. 'useLanguages' => $languages ? true : false,
  285. 'useHistory' => true
  286. ));
  287. if($pageID) {
  288. // resolved to a page
  289. if($languages) {
  290. $page = $this->wire('pages')->get($pageID);
  291. /** @var Language $language */
  292. $language = $this->wire('modules')->get('LanguageSupportPageNames')->getPagePathLanguage($path, $page);
  293. $pwid = !$language || $language->isDefault() ? $pageID : "$pageID-$language";
  294. } else {
  295. $language = null;
  296. $pwid = $pageID;
  297. }
  298. $replacements[$full] = "$start\tdata-pwid=$pwid$href$path$end";
  299. $counts['internal']++;
  300. if($this->debug) {
  301. $langName = $language ? $language->name : 'n/a';
  302. $this->message(
  303. "MarkupQA sleepLinks (field=$this->field, page={$this->page->path}, lang=$langName): " .
  304. "$full => " . $replacements[$full]
  305. );
  306. }
  307. } else {
  308. // did not resolve to a page, see if it resolves to a file or directory
  309. $file = $this->wire('config')->paths->root . ltrim($path, '/');
  310. if(file_exists($file)) {
  311. if($this->debug) $this->message("MarkupQA sleepLinks link resolved to a file: $path");
  312. $counts['files']++;
  313. } else {
  314. $parts = explode('/', trim($path, '/'));
  315. $firstPart = array_shift($parts);
  316. $test = $this->wire('config')->paths->root . $firstPart;
  317. if(is_dir($test)) {
  318. // possibly to something in another application, i.e. processwire.com/talk/
  319. $counts['other']++;
  320. } else {
  321. $counts['unresolved']++;
  322. $this->linkWarning($_path, false);
  323. }
  324. }
  325. }
  326. }
  327. if(count($replacements)) {
  328. $value = str_replace(array_keys($replacements), array_values($replacements), $value);
  329. }
  330. $info[$this->field->name] = $counts;
  331. if($this->verbose) $this->page->setQuietly('_markupQA', $info);
  332. }
  333. /**
  334. * Wakeup href attributes, using the data-pwid attribute to update the href attribute as necessary
  335. *
  336. * Should be used BEFORE wakeupUrls() is called so that href attributes are relative to "/" rather than
  337. * a potential "/subdir/" that wouldn't be recognized as a page path.
  338. *
  339. * @param $value
  340. *
  341. */
  342. public function wakeupLinks(&$value) {
  343. // if there's no data-pwid attribute present, then there's nothing to do here
  344. if(strpos($value, 'data-pwid=') === false) return;
  345. $re = '!' .
  346. '(<a[^\t<>]*?)' . // 1:"start" which includes "<a" and everything up until data-pwid attribute
  347. '\tdata-pwid=([-\d]+)' . // 2:"pwid" integer of page id ($pageID) referenced by the link
  348. '([\t ]+href=(?:["\'](?:https?:)?//[^/"\'\s<>]+|["\']))' . // 3:"href" attribute and optional scheme+hostname
  349. '([-_./a-z0-9]+)' . // 4:"path" in PW page name format
  350. '([^<>]*>)' . // 5:"end" which includes everything else and closing ">", i.e. query string, other attrs, etc.
  351. '!i';
  352. if(!preg_match_all($re, $value, $matches)) return;
  353. $replacements = array();
  354. $languages = $this->wire('languages');
  355. $rootURL = $this->wire('config')->urls->root;
  356. foreach($matches[2] as $key => $pwid) {
  357. if(strpos($pwid, '-')) {
  358. list($pageID, $languageID) = explode('-', $pwid);
  359. } else {
  360. $pageID = $pwid;
  361. $languageID = 0;
  362. }
  363. $full = $matches[0][$key];
  364. $start = $matches[1][$key];
  365. $href = $matches[3][$key];
  366. $path = $matches[4][$key];
  367. $end = $matches[5][$key];
  368. if($languages) {
  369. $language = $languageID ? $languages->get((int) $languageID) : $languages->getDefault();
  370. } else {
  371. $language = null;
  372. }
  373. $livePath = $this->wire('pages')->getPath((int) $pageID, array(
  374. 'language' => $language
  375. ));
  376. if(strlen($rootURL) > 1) {
  377. $livePath = rtrim($rootURL, '/') . $livePath;
  378. $href = ' ' . ltrim($href); // immunity to wakeupUrls(), replacing tab with space
  379. }
  380. $langName = $this->debug && $language ? $language->name : '';
  381. if($livePath) {
  382. if($path && substr($path, -1) != '/') {
  383. // no trailing slash, retain the editors wishes here
  384. $livePath = rtrim($livePath, '/');
  385. }
  386. if(strpos($livePath, '/trash/') !== false) {
  387. // linked page is in trash, we won't update it but we'll produce a warning
  388. $this->linkWarning("$path => $livePath (" . $this->_('it is in the trash') . ')');
  389. } else if($livePath != $path) {
  390. // path differs from what's in the markup and should be updated
  391. if($this->debug) $this->warning(
  392. "MarkupQA wakeupLinks PATH UPDATED (field=$this->field, page={$this->page->path}, " .
  393. "language=$langName): $path => $livePath"
  394. );
  395. $path = $livePath;
  396. } else if($this->debug) {
  397. $this->message("MarkupQA wakeupLinks no changes (field=$this->field, language=$langName): $path => $livePath");
  398. }
  399. } else {
  400. // did not resolve to a PW page
  401. $this->linkWarning("wakeup: $path");
  402. }
  403. $replacements[$full] = "$start$href$path$end";
  404. }
  405. if(count($replacements)) {
  406. $value = str_replace(array_keys($replacements), array_values($replacements), $value);
  407. }
  408. }
  409. /**
  410. * Display and log a warning about a path that didn't resolve
  411. *
  412. * @param string $path
  413. * @param bool $logWarning
  414. *
  415. */
  416. protected function linkWarning($path, $logWarning = true) {
  417. if($this->wire('page')->template == 'admin' && $this->wire('process') == 'ProcessPageEdit') {
  418. $this->warning(sprintf(
  419. $this->_('Unable to resolve link on page %1$s in field "%2$s": %3$s'),
  420. $this->page->path,
  421. $this->field->name,
  422. $path
  423. ));
  424. }
  425. if($this->verbose || $logWarning) {
  426. $this->error("Unable to resolve link: $path");
  427. }
  428. }
  429. /**
  430. * Quality assurance for <img> tags
  431. *
  432. * @param string $value
  433. * @param array $options What actions should be performed:
  434. * - replaceBlankAlt (bool): Replace blank alt attributes with file description? (default=true)
  435. * - removeNoExists (bool): Remove references to images that don't exist (or re-create images when possible) (default=true)
  436. * - removeNoAccess (bool): Remove references to images user doesn't have view permission to (default=true)
  437. *
  438. */
  439. public function checkImgTags(&$value, array $options = array()) {
  440. if(strpos($value, '<img ') !== false && preg_match_all('{(<img [^>]+>)}', $value, $matches)) {
  441. foreach($matches[0] as $key => $img) {
  442. $this->checkImgTag($value, $img, $options);
  443. }
  444. }
  445. }
  446. /**
  447. * Quality assurance for one <img> tag
  448. *
  449. * @param string $value Entire markup
  450. * @param string $img Just the found <img> tag
  451. * @param array $options What actions should be performed:
  452. * - replaceBlankAlt (bool): Replace blank alt attributes with file description? (default=true)
  453. * - removeNoExists (bool): Remove references to images that don't exist (or re-create images when possible) (default=true)
  454. * - removeNoAccess (bool): Remove references to images user doesn't have view permission to (default=true)
  455. *
  456. */
  457. protected function checkImgTag(&$value, $img, array $options = array()) {
  458. $defaults = array(
  459. 'replaceBlankAlt' => true,
  460. 'removeNoExists' => true,
  461. 'removeNoAccess' => true,
  462. );
  463. $options = array_merge($defaults, $options);
  464. $replaceAlt = ''; // exact text to replace for blank alt attribute, i.e. alt=""
  465. $src = '';
  466. $user = $this->wire('user');
  467. $attrStrings = explode(' ', $img); // array of strings like "key=value"
  468. if($this->verbose) {
  469. $markupQA = $this->page->get('_markupQA');
  470. if(!is_array($markupQA)) $markupQA = array();
  471. if(!isset($markupQA[$this->field->name])) $markupQA[$this->field->name] = array();
  472. $info =& $markupQA[$this->field->name];
  473. } else {
  474. $markupQA = null;
  475. $info = array();
  476. }
  477. if(!isset($info['img_unresolved'])) $info['img_unresolved'] = 0;
  478. if(!isset($info['img_fixed'])) $info['img_fixed'] = 0;
  479. if(!isset($info['img_noalt'])) $info['img_noalt'] = 0; // blank alt
  480. // determine current 'alt' and 'src' attributes
  481. foreach($attrStrings as $n => $attr) {
  482. if(!strpos($attr, '=')) continue;
  483. list($name, $val) = explode('=', $attr);
  484. $name = strtolower($name);
  485. $val = trim($val, "\"' ");
  486. if($name == 'alt' && !strlen($val)) {
  487. $replaceAlt = $attr;
  488. } else if($name == 'src') {
  489. $src = $val;
  490. }
  491. }
  492. // if <img> had no src attr, or if it was pointing to something outside of PW assets, skip it
  493. if(!$src || strpos($src, $this->assetsURL) === false) return;
  494. // recognized site image, make sure the file exists
  495. /** @var Pageimage $pagefile */
  496. $pagefile = $this->page->filesManager()->getFile($src);
  497. // if this doesn't resolve to a known pagefile, stop now
  498. if(!$pagefile) {
  499. if($options['removeNoExists']) {
  500. if(file_exists($this->page->filesManager()->path() . basename($src))) {
  501. // file exists, but we just don't know what it is - leave it alone
  502. } else {
  503. $this->error("Image file no longer exists: " . basename($src) . ")");
  504. if($this->page->of()) $value = str_replace($img, '', $value);
  505. $info['img_unresolved']++;
  506. }
  507. }
  508. return;
  509. }
  510. if($options['removeNoAccess']) {
  511. if(($pagefile->page->id != $this->page->id && !$user->hasPermission('page-view', $pagefile->page))
  512. || ($pagefile->field && !$pagefile->page->viewable($pagefile->field))) {
  513. // if the file resolves to another page that the user doesn't have access to view,
  514. // OR user doesn't have permission to view the field that $pagefile is in,
  515. // then we will simply remove the image
  516. $this->error("Image referenced that user does not have view access to: $src");
  517. if($this->page->of()) $value = str_replace($img, '', $value);
  518. return;
  519. }
  520. }
  521. if($options['replaceBlankAlt'] && $replaceAlt) {
  522. // image has a blank alt tag, meaning, we will auto-populate it with current file description,
  523. // if output formatting is on
  524. if($this->page->of()) {
  525. $alt = $pagefile->description;
  526. if(strlen($alt)) {
  527. $alt = $this->wire('sanitizer')->entities1($alt);
  528. $_img = str_replace(" $replaceAlt", " alt=\"$alt\"", $img);
  529. $value = str_replace($img, $_img, $value);
  530. }
  531. }
  532. $info['img_noalt']++;
  533. }
  534. if($options['removeNoExists'] && $pagefile instanceof Pageimage) {
  535. $result = $this->checkImgExists($pagefile, $img, $src, $value);
  536. if($result < 0) $info['img_unresolved'] += abs($result);
  537. if($result > 0) $info['img_fixed'] += $result;
  538. }
  539. if($markupQA) $this->page->setQuietly('_markupQA', $markupQA);
  540. }
  541. /**
  542. * Attempt to re-create images that don't exist, when possible
  543. *
  544. * @param Pageimage $pagefile
  545. * @param $img
  546. * @param $src
  547. * @param $value
  548. * @return int Returns 0 on no change, negative count on broken, positive count on fixed
  549. *
  550. */
  551. protected function checkImgExists(Pageimage $pagefile, $img, $src, &$value) {
  552. $basename = basename($src);
  553. $pathname = $pagefile->pagefiles->path() . $basename;
  554. if(file_exists($pathname)) return 0; // no action necessary
  555. // file referenced in <img> tag does not exist, and it is not a variation we can re-create
  556. if($pagefile->basename == $basename) {
  557. // original file no longer exists
  558. $this->error("Original image file no longer exists, unable to create new variation ($basename)");
  559. if($this->page->of()) $value = str_replace($img, '', $value); // remove reference to image, when output formatting is on
  560. return -1;
  561. }
  562. // check if this is a variation that we might be able to re-create
  563. $info = $pagefile->isVariation($basename);
  564. if(!$info) {
  565. // file is not a variation, so we apparently have no source to pull info from
  566. $this->error("Unrecognized image that does not exist ($basename)");
  567. if($this->page->of()) $value = str_replace($img, '', $value); // remove reference to image, when output formatting is on
  568. return -1;
  569. }
  570. $info['targetName'] = $basename;
  571. $variations = array($info);
  572. while(!empty($info['parent'])) {
  573. $variations[] = $info['parent'];
  574. $info = $info['parent'];
  575. }
  576. $good = 0;
  577. $bad = 0;
  578. foreach(array_reverse($variations) as $info) {
  579. // definitely a variation, attempt to re-create it
  580. $options = array();
  581. if($info['crop']) $options['cropping'] = $info['crop'];
  582. if($info['suffix']) {
  583. $options['suffix'] = $info['suffix'];
  584. if(in_array('hidpi', $options['suffix'])) $options['hidpi'] = true;
  585. }
  586. /** @var Pageimage $newPagefile */
  587. $newPagefile = $pagefile->size($info['width'], $info['height'], $options);
  588. if($newPagefile && is_file($newPagefile->filename())) {
  589. if(!empty($info['targetName']) && $newPagefile->basename != $info['targetName']) {
  590. // new name differs from what is in text. Rename file to be consistent with text.
  591. rename($newPagefile->filename(), $pathname);
  592. }
  593. if($this->debug || $this->wire('config')->debug) {
  594. $this->message($this->_('Re-created image variation') . " - $newPagefile->name");
  595. }
  596. $pagefile = $newPagefile; // for next iteration
  597. $good++;
  598. } else {
  599. $this->error($this->_('Unable to re-create image variation') . " - $newPagefile->name");
  600. $bad++;
  601. }
  602. }
  603. if($good) return $good;
  604. if($bad) return -1 * $bad;
  605. return 0;
  606. }
  607. /**
  608. * Record error message to image-errors log
  609. *
  610. * @param string $text
  611. * @param int $flags
  612. * @return $this
  613. *
  614. */
  615. public function error($text, $flags = 0) {
  616. $logText = "$text (page={$this->page->path}, field={$this->field->name})";
  617. $this->wire('log')->save(self::errorLogName, $logText);
  618. /*
  619. if($this->wire('modules')->isInstalled('SystemNotifications')) {
  620. $user = $this->wire('modules')->get('SystemNotifications')->getSystemUser();
  621. if($user && !$user->notifications->getBy('title', $text)) {
  622. $no = $user->notifications()->getNew('error');
  623. $no->title = $text;
  624. $no->html = "<p>Field: {$this->field->name}\n<br />Page: <a href='{$this->page->url}'>{$this->page->title}</a></p>";
  625. $user->notifications->save();
  626. }
  627. }
  628. */
  629. return $this;
  630. }
  631. }