1<?php 2/** 3 * Matomo - free/libre analytics platform 4 * 5 * @link https://matomo.org 6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later 7 */ 8 9namespace Piwik\Plugins\CustomJsTracker; 10 11use Piwik\Common; 12use Piwik\Container\StaticContainer; 13use Piwik\Plugins\CustomJsTracker\TrackingCode\PiwikJsManipulator; 14use Piwik\Plugins\CustomJsTracker\TrackingCode\PluginTrackerFiles; 15use Piwik\Piwik; 16 17/** 18 * Updates the Piwik JavaScript Tracker "piwik.js" in case plugins extend the tracker. 19 * 20 * Usage: 21 * StaticContainer::get('Piwik\Plugins\CustomJsTracker\TrackerUpdater')->update(); 22 */ 23class TrackerUpdater 24{ 25 const DEVELOPMENT_PIWIK_JS = '/js/piwik.js'; 26 const ORIGINAL_PIWIK_JS = '/js/piwik.min.js'; 27 const TARGET_MATOMO_JS = '/matomo.js'; 28 29 /** 30 * @var File 31 */ 32 private $fromFile; 33 34 /** 35 * @var File 36 */ 37 private $toFile; 38 39 private $trackerFiles = array(); 40 41 /** 42 * @param string|null $fromFile If null then the minified JS tracker file in /js fill be used 43 * @param string|null $toFile If null then the minified JS tracker will be updated. 44 */ 45 public function __construct($fromFile = null, $toFile = null) 46 { 47 if (!isset($fromFile)) { 48 $fromFile = PIWIK_DOCUMENT_ROOT . self::ORIGINAL_PIWIK_JS; 49 } 50 51 if (!isset($toFile)) { 52 $toFile = PIWIK_DOCUMENT_ROOT . self::TARGET_MATOMO_JS; 53 } 54 55 $this->setFromFile($fromFile); 56 $this->setToFile($toFile); 57 $this->trackerFiles = StaticContainer::get('Piwik\Plugins\CustomJsTracker\TrackingCode\PluginTrackerFiles'); 58 } 59 60 public function setFromFile($fromFile) 61 { 62 if (is_string($fromFile)) { 63 $fromFile = new File($fromFile); 64 } 65 $this->fromFile = $fromFile; 66 } 67 68 public function getFromFile() 69 { 70 return $this->fromFile; 71 } 72 73 public function setToFile($toFile) 74 { 75 if (is_string($toFile)) { 76 $toFile = new File($toFile); 77 } 78 $this->toFile = $toFile; 79 } 80 81 public function getToFile() 82 { 83 return $this->toFile; 84 } 85 86 public function setTrackerFiles(PluginTrackerFiles $trackerFiles) 87 { 88 $this->trackerFiles = $trackerFiles; 89 } 90 91 /** 92 * Checks whether the Piwik JavaScript tracker file "piwik.js" is writable. 93 * @throws \Exception In case the piwik.js file is not writable. 94 * 95 * @api 96 */ 97 public function checkWillSucceed() 98 { 99 $this->fromFile->checkReadable(); 100 $this->toFile->checkWritable(); 101 } 102 103 public function getCurrentTrackerFileContent() 104 { 105 return $this->toFile->getContent(); 106 } 107 108 public function getUpdatedTrackerFileContent() 109 { 110 $trackingCode = new PiwikJsManipulator($this->fromFile->getContent(), $this->trackerFiles); 111 $newContent = $trackingCode->manipulateContent(); 112 113 return $newContent; 114 } 115 116 /** 117 * Updates / re-generates the Piwik JavaScript tracker "piwik.js". 118 * 119 * It may not be possible to update the "piwik.js" tracker file if the file is not writable. It won't throw 120 * an exception in such a case and instead just to nothing. To check if the update would succeed, call 121 * {@link checkWillSucceed()}. 122 * 123 * @api 124 */ 125 public function update() 126 { 127 if (!$this->toFile->hasWriteAccess() || !$this->fromFile->hasReadAccess()) { 128 return; 129 } 130 131 $newContent = $this->getUpdatedTrackerFileContent(); 132 133 if (!$this->toFile->isFileContentSame($newContent)) { 134 $savedFiles = $this->toFile->save($newContent); 135 foreach ($savedFiles as $savedFile) { 136 137 /** 138 * Triggered after the tracker JavaScript content (the content of the piwik.js file) is changed. 139 * 140 * @param string $absolutePath The path to the new piwik.js file. 141 */ 142 Piwik::postEvent('CustomJsTracker.trackerJsChanged', [$savedFile]); 143 } 144 145 } 146 147 // we need to make sure to sync matomo.js / piwik.js 148 $this->updateAlternative('piwik.js', 'matomo.js', $newContent); 149 $this->updateAlternative('matomo.js', 'piwik.js', $newContent); 150 } 151 152 private function updateAlternative($fromFile, $toFile, $newContent) 153 { 154 if (Common::stringEndsWith($this->toFile->getName(), $fromFile)) { 155 $alternativeFilename = dirname($this->toFile->getPath()) . DIRECTORY_SEPARATOR . $toFile; 156 $file = $this->toFile->setFile($alternativeFilename); 157 if ($file->hasWriteAccess() && !$file->isFileContentSame($newContent)) { 158 $savedFiles = $file->save($newContent); 159 foreach ($savedFiles as $savedFile) { 160 Piwik::postEvent('CustomJsTracker.trackerJsChanged', [$savedFile]); 161 } 162 } 163 } 164 } 165} 166