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 */ 8namespace Piwik\Plugins\TagManager\Dao; 9 10 11use Piwik\Db; 12use Piwik\DbHelper; 13use Piwik\Piwik; 14use Exception; 15use Piwik\Plugins\TagManager\Input\Description; 16use Piwik\Plugins\TagManager\Input\Name; 17 18class ContainerVersionsDao extends BaseDao implements TagManagerDao 19{ 20 const REVISION_DRAFT = 0; 21 22 protected $table = 'tagmanager_container_version'; 23 24 public function install() 25 { 26 DbHelper::createTable($this->table, " 27 `idcontainerversion` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, 28 `idcontainer` VARCHAR(8) NOT NULL, 29 `idsite` int(11) UNSIGNED NOT NULL, 30 `status` VARCHAR(10) NOT NULL, 31 `revision` MEDIUMINT UNSIGNED NOT NULL DEFAULT 1, 32 `name` VARCHAR(" . Name::MAX_LENGTH . ") NOT NULL DEFAULT '', 33 `description` VARCHAR(" . Description::MAX_LENGTH . ") NOT NULL DEFAULT '', 34 `created_date` DATETIME NOT NULL, 35 `updated_date` DATETIME NOT NULL, 36 `deleted_date` DATETIME NULL, 37 PRIMARY KEY(`idcontainerversion`), KEY(`idcontainer`), KEY (`idsite`, `idcontainer`)"); 38 // we cannot set a unique key on (`idsite`, `idcontainerversion`, `name`) because we soft delete tags and want to make sure names can be used again after deleting an entry 39 } 40 41 private function isNameInUse($idSite, $idContainer, $name, $exceptIdVersion = null) 42 { 43 $sql = sprintf("SELECT idcontainerversion FROM %s WHERE idsite = ? AND idcontainer = ? AND `name` = ? AND status = ?", $this->tablePrefixed); 44 $bind = array($idSite, $idContainer, $name, self::STATUS_ACTIVE); 45 46 if (!empty($exceptIdVersion)) { 47 $sql .= ' AND idcontainerversion != ?'; 48 $bind[] = $exceptIdVersion; 49 } 50 51 $idSite = Db::fetchOne($sql, $bind); 52 return !empty($idSite); 53 } 54 55 public function getNextRevisionOfContainer($idSite, $idContainer) 56 { 57 $sql = "SELECT max(revision) FROM " . $this->tablePrefixed . " WHERE idsite = ? and idcontainer = ?"; 58 $revision = Db::fetchOne($sql, array($idSite, $idContainer)); 59 60 if (empty($revision)) { 61 return 1; 62 } 63 64 return $revision + 1; 65 } 66 67 private function hasDraftVersionAlready($idSite, $idContainer) 68 { 69 $sql = sprintf("SELECT idsite FROM %s WHERE idsite = ? AND idcontainer = ? AND `revision` = 0 AND status = ?", $this->tablePrefixed); 70 $bind = array($idSite, $idContainer, self::STATUS_ACTIVE); 71 72 $idSite = Db::fetchOne($sql, $bind); 73 return !empty($idSite); 74 } 75 76 public function createDraftVersion($idSite, $idContainer, $createdDate) 77 { 78 if ($this->hasDraftVersionAlready($idSite, $idContainer)) { 79 throw new Exception('A draft version for this container already exists'); 80 } 81 $values = array( 82 'idcontainer' => $idContainer, 83 'idsite' => $idSite, 84 'status' => self::STATUS_ACTIVE, 85 'created_date' => $createdDate, 86 'updated_date' => $createdDate, 87 'revision' => self::REVISION_DRAFT 88 ); 89 90 return $this->insertRecord($values); 91 } 92 93 public function createVersion($idSite, $idContainer, $versionName, $versionDescription, $createdDate) 94 { 95 if ($this->isNameInUse($idSite, $idContainer, $versionName)) { 96 throw new Exception(Piwik::translate('TagManager_ErrorNameDuplicate')); 97 } 98 99 $revision = $this->getNextRevisionOfContainer($idSite, $idContainer); 100 101 $values = array( 102 'idcontainer' => $idContainer, 103 'idsite' => $idSite, 104 'status' => self::STATUS_ACTIVE, 105 'name' => $versionName, 106 'description' => !empty($versionDescription) ? $versionDescription : '', 107 'created_date' => $createdDate, 108 'updated_date' => $createdDate, 109 'revision' => $revision 110 ); 111 112 return $this->insertRecord($values); 113 } 114 115 /** 116 * @param int $idSite 117 * @param array $statuses 118 * @return array 119 */ 120 public function getVersionsOfContainer($idSite, $idContainer) 121 { 122 $bind = array(self::STATUS_ACTIVE, $idSite, $idContainer); 123 124 $table = $this->tablePrefixed; 125 $versions = Db::fetchAll("SELECT * FROM $table WHERE status = ? AND idsite = ? and idcontainer = ? and revision > 0 order by revision desc", $bind); 126 127 return $this->enrichVersions($versions); 128 } 129 130 /** 131 * @param int $idSite 132 * @param array $statuses 133 * @return array 134 */ 135 public function getDraftVersion($idSite, $idContainer) 136 { 137 $bind = array(self::STATUS_ACTIVE, $idSite, $idContainer); 138 139 $table = $this->tablePrefixed; 140 $version = Db::fetchRow("SELECT * FROM $table WHERE status = ? AND idsite = ? and idcontainer = ? and revision = 0 LIMIT 1", $bind); 141 142 return $this->enrichVersion($version); 143 } 144 145 /** 146 * @param int $idSite 147 * @param array $statuses 148 * @return array 149 */ 150 public function getVersion($idSite, $idContainer, $idContainerVersion) 151 { 152 $bind = array(self::STATUS_ACTIVE, $idSite, $idContainer, $idContainerVersion); 153 154 $table = $this->tablePrefixed; 155 $version = Db::fetchRow("SELECT * FROM $table WHERE status = ? AND idsite = ? and idcontainer = ? and idcontainerversion = ? LIMIT 1", $bind); 156 157 return $this->enrichVersion($version); 158 } 159 160 public function updateContainerColumns($idSite, $idContainer, $idContainerVersion, $columns) 161 { 162 if (!empty($columns)) { 163 if (isset($columns['description']) && empty($columns['description'])) { 164 $columns['description'] = ''; 165 } 166 if (isset($columns['name']) && $this->isNameInUse($idSite, $idContainer, $columns['name'], $idContainerVersion)) { 167 throw new Exception(Piwik::translate('TagManager_ErrorNameDuplicate')); 168 } 169 170 $this->updateEntity($columns, ['idsite' => (int)$idSite, 'idcontainer' => $idContainer, 'idcontainerversion' => (int)$idContainerVersion]); 171 } 172 } 173 174 public function getAllVersions() 175 { 176 $containers = Db::fetchAll('SELECT * FROM ' . $this->tablePrefixed . ' ORDER BY idcontainerversion ASC'); 177 return $this->enrichVersions($containers); 178 } 179 180 /** 181 * @param int $idSite 182 * @param string $deletedDate 183 */ 184 public function deleteAllVersionsForSite($idSite, $deletedDate) 185 { 186 $table = $this->tablePrefixed; 187 188 $query = "UPDATE $table SET status = ?, deleted_date = ? WHERE idsite = ? and status != ?"; 189 $bind = array(self::STATUS_DELETED, $deletedDate, $idSite, self::STATUS_DELETED); 190 191 Db::query($query, $bind); 192 } 193 194 /** 195 * @param int $idSite 196 * @param int $idContainerVersion 197 * @param string $deletedDate 198 */ 199 public function deleteVersion($idSite, $idContainerVersion, $deletedDate) 200 { 201 $table = $this->tablePrefixed; 202 203 $query = "UPDATE $table SET status = ?, deleted_date = ? WHERE idsite = ? and idcontainerversion = ? and status != ?"; 204 $bind = array(self::STATUS_DELETED, $deletedDate, $idSite, $idContainerVersion, self::STATUS_DELETED); 205 206 Db::query($query, $bind); 207 } 208 209 private function enrichVersions($containers) 210 { 211 if (empty($containers)) { 212 return array(); 213 } 214 215 foreach ($containers as $index => $container) { 216 $containers[$index] = $this->enrichVersion($container); 217 } 218 219 return $containers; 220 } 221 222 private function enrichVersion($container) 223 { 224 if (empty($container)) { 225 return $container; 226 } 227 228 $container['idcontainerversion'] = (int) $container['idcontainerversion']; 229 $container['revision'] = (int) $container['revision']; 230 $container['idsite'] = (int) $container['idsite']; 231 232 return $container; 233 } 234} 235 236