Skip to content

Commit

Permalink
Merged with 5.x-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
AltamashShaikh committed Nov 26, 2024
2 parents 8fd297b + 6accb07 commit bce46c9
Show file tree
Hide file tree
Showing 50 changed files with 1,496 additions and 230 deletions.
66 changes: 56 additions & 10 deletions Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use Piwik\API\Request;
use Piwik\Common;
use Piwik\Container\StaticContainer;
use Piwik\DataTable\Filter\SafeDecodeLabel;
use Piwik\Filechecks;
use Piwik\Menu\MenuTop;
Expand All @@ -21,6 +22,7 @@
use Piwik\Plugins\TagManager\Input\AccessValidator;
use Piwik\Plugins\TagManager\Model\Container;
use Piwik\Plugins\TagManager\Model\Environment;
use Piwik\Plugins\TagManager\Model\Tag;
use Piwik\Site;
use Piwik\Url;
use Piwik\View;
Expand All @@ -29,6 +31,7 @@
class Controller extends \Piwik\Plugin\Controller
{
public const COPY_CONTAINER_NONCE = 'TagManager.copyContainer';
public const COPY_TAG_NONCE = 'TagManager.copyTag';

/**
* @var AccessValidator
Expand Down Expand Up @@ -385,15 +388,58 @@ public function copyContainer()
'idSite' => $idDestinationSite,
'idContainer' => $idContainerNew
]);
$successMessage = Piwik::translate('TagManager_ContainerCopySuccess', ['<a href="' . $url . '">', '</a>']);
$notification = new Notification($successMessage);
$notification->raw = true;
$notification->context = Notification::CONTEXT_SUCCESS;
$notification->type = Notification::TYPE_TRANSIENT;
Notification\Manager::notify('containerCopied', $notification);

// Once the copy is done, we should be able to redirect to the manage screen
$this->redirectToIndex('TagManager', 'manageContainers', $this->idSite);

return json_encode(['isSuccess' => true, 'urlToNewCopy' => $url]);
}

public function copyTagDialog()
{
$this->accessValidator->checkWriteCapability($this->idSite);
$this->accessValidator->checkUseCustomTemplatesCapability($this->idSite);

$request = \Piwik\Request::fromRequest();
$idTag = $request->getIntegerParameter('idTag');
$idSourceContainer = $request->getStringParameter('idContainer');
$idContainerVersion = $request->getIntegerParameter('idContainerVersion');

$view = new View("@TagManager/copyDialog");
$view->defaultSiteDecoded = [
'id' => $this->idSite,
'name' => Common::unsanitizeInputValue(Site::getNameFor($this->idSite)),
];
$view->idToCopy = $idTag;
$view->copyType = 'tag';
$view->idSourceContainer = $idSourceContainer;
$view->idContainerVersion = $idContainerVersion;
$view->copyNonce = Nonce::getNonce(self::COPY_TAG_NONCE);
return $view->render();
}

public function copyTag()
{
$this->accessValidator->checkWriteCapability($this->idSite);
$this->accessValidator->checkUseCustomTemplatesCapability($this->idSite);
Nonce::checkNonce(self::COPY_TAG_NONCE);

$request = \Piwik\Request::fromRequest();
$idDestinationSite = $request->getIntegerParameter('idDestinationSite');
$idDestinationContainer = $request->getStringParameter('idDestinationContainer');
// Confirm tha the user has permission to copy to the selected site
$this->accessValidator->checkWriteCapability($idDestinationSite);
$idTag = $request->getIntegerParameter('idTag');
$idContainerVersion = $request->getIntegerParameter('idContainerVersion');

$idTagNew = StaticContainer::get(Tag::class)->copyTag($this->idSite, $idContainerVersion, $idTag, $idDestinationSite, $idDestinationContainer);

$url = 'index.php?module=TagManager&action=manageTags&'
. Url::getQueryStringFromParameters([
'idSite' => $idDestinationSite,
'idContainer' => $idDestinationContainer
]) . '#?' . Url::getQueryStringFromParameters([
'idTag' => $idTagNew,
]);

return json_encode(['isSuccess' => true, 'urlToNewCopy' => $url]);
}

protected function renderTemplate($template, array $variables = array())
Expand All @@ -411,7 +457,7 @@ protected function renderTemplate($template, array $variables = array())
$view->topMenu = MenuTop::getInstance()->getMenu();
$view->tagManagerMenu = MenuTagManager::getInstance()->getMenu();

list($defaultAction, $defaultParameters) = Menu::getDefaultAction();
[$defaultAction, $defaultParameters] = Menu::getDefaultAction();
$view->tagAction = $defaultAction;

foreach ($variables as $key => $value) {
Expand Down
84 changes: 84 additions & 0 deletions Dao/BaseDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Piwik\Common;
use Piwik\Date;
use Piwik\Db;
use Piwik\Plugins\TagManager\Input\Name;

abstract class BaseDao
{
Expand Down Expand Up @@ -73,8 +74,91 @@ public function updateEntity($columns, $whereColumns)
}
}

/**
* Make sure that the name is unique. This means appending a number at the end, or if there's already been a number
* appended, increment the previous number. This way, when copying a tag/trigger/variable to the same site, we don't
* get an error that the name is already in use.
*
* @param int $idSite
* @param string $name
* @param null|int $idContainerVersion Optional ID of the container version. It's only optional since containers
* don't need it.
* @return string
* @throws \Exception Throws an exception if it's a Tag, Trigger, or Variable and doesn't have a idContainerVersion
*/
public function makeCopyNameUnique(int $idSite, string $name, ?int $idContainerVersion = null): string
{
$requireVersion = [
'Piwik\Plugins\TagManager\Dao\TagsDao',
'Piwik\Plugins\TagManager\Dao\TriggersDao',
'Piwik\Plugins\TagManager\Dao\VariablesDao',
'Piwik\Plugins\TagManager\Dao\ContainerVersionsDao',
];
if (in_array(get_class($this), $requireVersion) && $idContainerVersion === null) {
throw new \Exception('The idContainerVersion is required for Tags, Triggers, and Variables');
}

// If the name isn't already in use, simply return it
if (!$this->isNameAlreadyUsed($idSite, $name, $idContainerVersion)) {
return $name;
}

$newName = $this->incrementNameWithNumber($name);

// Make sure that the new name doesn't already exist
// Call this method recursively until we have a unique name
if ($this->isNameAlreadyUsed($idSite, $newName, $idContainerVersion)) {
$newName = $this->makeCopyNameUnique($idSite, $newName, $idContainerVersion);
}

return $newName;
}

/**
* Check if the name is already in use. If it's a container, the idContainerVersion isn't needed. It's required for
* tags, triggers, and variables.
*
* @param int $idSite
* @param string $name
* @param null|int $idContainerVersion Optional ID of the container version. It's only optional since containers
* don't need it.
* @return bool Indicating whether the name is already in use
*/
abstract protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool;

protected function getCurrentDateTime()
{
return Date::now()->getDatetime();
}

/**
* Update the provided name with a number suffix. It will either add a suffix or increment the number in the suffix.
*
* @param string $name The name that needs to be updated with a number suffix. If no suffix exists, one will be
* added. If one already exists, the number in the suffix will be incremented.
* @return string Name with the updated number suffix
*/
protected function incrementNameWithNumber(string $name): string
{
$newName = $name;

// First check if the name already has a number suffix
$matches = [];
$number = 1;
if (preg_match('/ \(\d+\)$/', $name, $matches)) {
// Increment the number in the name suffix
$number = intval(str_replace(['(', ')'], '', $matches[0]));
++$number;
$newName = str_replace($matches[0], '', $name);
}

// Make sure that we don't exceed the max length the name fields
if (strlen($newName . " ($number)") > Name::MAX_LENGTH) {
$newName = substr($newName, 0, (Name::MAX_LENGTH - 3) - strlen((string) $number));
}

$newName .= " ($number)";

return $newName;
}
}
6 changes: 6 additions & 0 deletions Dao/ContainerReleaseDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ public function deleteNoLongerExistingEnvironmentReleases($availableEnvironments
return $query->rowCount();
}

protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool
{
// This is hard coded since releases don't have a name and therefore don't use this method
return true;
}

private function enrichReleases($releases)
{
if (empty($releases)) {
Expand Down
13 changes: 13 additions & 0 deletions Dao/ContainerVersionsDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,19 @@ public function deleteVersion($idSite, $idContainerVersion, $deletedDate)
Db::query($query, $bind);
}

protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool
{
// Look up the container ID using the version ID
$bind = array(self::STATUS_ACTIVE, $idSite, $idContainerVersion);
$table = $this->tablePrefixed;
$version = Db::fetchRow("SELECT idcontainer FROM $table WHERE status = ? AND idsite = ? AND idcontainerversion = ? LIMIT 1", $bind);
if (empty($version['idcontainer'])) {
return false;
}

return $this->isNameInUse($idSite, $version['idcontainer'], $name);
}

private function enrichVersions($containers)
{
if (empty($containers)) {
Expand Down
37 changes: 2 additions & 35 deletions Dao/ContainersDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -205,42 +205,9 @@ public function deleteContainer($idSite, $idContainer, $deletedDate)
Db::query($query, $bind);
}

/**
* Make sure that the container name is unique. This means appending a number at the end, or if there's already been
* a number appended, increment the previous number. This way, when copying a container to the same site, we don't
* get an error that the name is already in use.
*
* @param int $idSite
* @param string $containerName
* @return string
*/
public function makeCopyNameUnique(int $idSite, string $containerName): string
protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool
{
// If the name isn't already in use, simply return it
if (!$this->isNameInUse($idSite, $containerName)) {
return $containerName;
}

$newContainerName = $containerName;

// First check if the container name has already been automatically updated during copy process
$matches = [];
$number = 1;
if (preg_match('/ \(\d+\)$/', $containerName, $matches)) {
// Increment the number in the name
$number = intval(str_replace(['(', ')'], '', $matches[0]));
++$number;
$newContainerName = str_replace($matches[0], '', $containerName);
}
$newContainerName .= " ($number)";

// Make sure that the new name doesn't already exist
// Call this method recursively until we have a unique name
if ($this->isNameInUse($idSite, $newContainerName)) {
$newContainerName = $this->makeCopyNameUnique($idSite, $newContainerName);
}

return $newContainerName;
return $this->isNameInUse($idSite, $name);
}

private function enrichContainers($containers)
Expand Down
5 changes: 5 additions & 0 deletions Dao/TagsDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ private function isNameInUse($idSite, $idContainerVersion, $name, $exceptIdTag =
return !empty($idSite);
}

protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool
{
return $this->isNameInUse($idSite, $idContainerVersion, $name);
}

public function createTag($idSite, $idContainerVersion, $type, $name, $parameters, $fireTriggerIds, $blockTriggerIds, $fireLimit, $fireDelay, $priority, $startDate, $endDate, $createdDate, $description = '', $status = '')
{
if ($this->isNameInUse($idSite, $idContainerVersion, $name)) {
Expand Down
23 changes: 23 additions & 0 deletions Dao/TriggersDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ public function getContainerTrigger($idSite, $idContainerVersion, $idTrigger)
return $this->enrichTrigger($trigger);
}

/**
* Look up the trigger using its name
*
* @param int $idSite
* @param int $idContainerVersion
* @param string $triggerName
* @return array|false
* @throws \Exception
*/
public function findTriggerByName(int $idSite, int $idContainerVersion, string $triggerName)
{
$table = $this->tablePrefixed;
$bind = array($idSite, $idContainerVersion, self::STATUS_ACTIVE, $triggerName);
$trigger = Db::fetchRow("SELECT * FROM $table WHERE idsite = ? AND idcontainerversion = ? AND status = ? AND `name` = ?", $bind);

return $this->enrichTrigger($trigger);
}

/**
* @param int $idSite
* @param string $deletedDate
Expand Down Expand Up @@ -174,6 +192,11 @@ public function deleteContainerTrigger($idSite, $idContainerVersion, $idTrigger,
Db::query($query, $bind);
}

protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool
{
return $this->isNameInUse($idSite, $idContainerVersion, $name);
}

private function enrichTriggers($triggers)
{
if (empty($triggers)) {
Expand Down
5 changes: 5 additions & 0 deletions Dao/VariablesDao.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ public function getContainerVariableIdsByType($idSite, $idContainerVersion, $var
return is_array($variables) && count($variables) ? array_column($variables, 'idvariable') : [];
}

protected function isNameAlreadyUsed(int $idSite, string $name, ?int $idContainerVersion = null): bool
{
return $this->isNameInUse($idSite, $idContainerVersion, $name);
}

private function enrichVariables($variables)
{
if (empty($variables)) {
Expand Down
Loading

0 comments on commit bce46c9

Please sign in to comment.