Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added the ability to copy triggers and variables #936

Merged
merged 11 commits into from
Nov 28, 2024
110 changes: 110 additions & 0 deletions Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
use Piwik\Plugins\TagManager\Model\Container;
use Piwik\Plugins\TagManager\Model\Environment;
use Piwik\Plugins\TagManager\Model\Tag;
use Piwik\Plugins\TagManager\Model\Trigger;
use Piwik\Plugins\TagManager\Model\Variable;
use Piwik\Site;
use Piwik\Url;
use Piwik\View;
Expand All @@ -32,6 +34,8 @@ class Controller extends \Piwik\Plugin\Controller
{
public const COPY_CONTAINER_NONCE = 'TagManager.copyContainer';
public const COPY_TAG_NONCE = 'TagManager.copyTag';
public const COPY_TRIGGER_NONCE = 'TagManager.copyTrigger';
public const COPY_VARIABLE_NONCE = 'TagManager.copyVariable';

/**
* @var AccessValidator
Expand Down Expand Up @@ -394,6 +398,7 @@ public function copyContainer()

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

Expand All @@ -417,6 +422,7 @@ public function copyTagDialog()

public function copyTag()
{
$this->checkSitePermission();
$this->accessValidator->checkWriteCapability($this->idSite);
$this->accessValidator->checkUseCustomTemplatesCapability($this->idSite);
Nonce::checkNonce(self::COPY_TAG_NONCE);
Expand All @@ -442,6 +448,110 @@ public function copyTag()
return json_encode(['isSuccess' => true, 'urlToNewCopy' => $url]);
}

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

$request = \Piwik\Request::fromRequest();
$idTrigger = $request->getIntegerParameter('idTrigger');
$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 = $idTrigger;
$view->copyType = 'trigger';
$view->idSourceContainer = $idSourceContainer;
$view->idContainerVersion = $idContainerVersion;
$view->copyNonce = Nonce::getNonce(self::COPY_TRIGGER_NONCE);
return $view->render();
}

public function copyTrigger()
{
$this->checkSitePermission();
$this->accessValidator->checkWriteCapability($this->idSite);
$this->accessValidator->checkUseCustomTemplatesCapability($this->idSite);
Nonce::checkNonce(self::COPY_TRIGGER_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);
$idTrigger = $request->getIntegerParameter('idTrigger');
$idContainerVersion = $request->getIntegerParameter('idContainerVersion');

$idTriggerNew = StaticContainer::get(Trigger::class)->copyTrigger($this->idSite, $idContainerVersion, $idTrigger, $idDestinationSite, $idDestinationContainer);

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

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

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

$request = \Piwik\Request::fromRequest();
$idVariable = $request->getIntegerParameter('idVariable');
$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 = $idVariable;
$view->copyType = 'variable';
$view->idSourceContainer = $idSourceContainer;
$view->idContainerVersion = $idContainerVersion;
$view->copyNonce = Nonce::getNonce(self::COPY_VARIABLE_NONCE);
return $view->render();
}

public function copyVariable()
{
$this->checkSitePermission();
$this->accessValidator->checkWriteCapability($this->idSite);
$this->accessValidator->checkUseCustomTemplatesCapability($this->idSite);
Nonce::checkNonce(self::COPY_VARIABLE_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);
$idVariable = $request->getIntegerParameter('idVariable');
$idContainerVersion = $request->getIntegerParameter('idContainerVersion');

$idVariableNew = StaticContainer::get(Variable::class)->copyVariable($this->idSite, $idContainerVersion, $idVariable, $idDestinationSite, $idDestinationContainer);

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

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

protected function renderTemplate($template, array $variables = array())
{
if (false === strpos($template, '@') || false === strpos($template, '/')) {
Expand Down
19 changes: 19 additions & 0 deletions Model/BaseModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

namespace Piwik\Plugins\TagManager\Model;

use Piwik\Container\StaticContainer;
use Piwik\Date;
use Piwik\Piwik;
use Piwik\Site;

class BaseModel
Expand Down Expand Up @@ -38,4 +40,21 @@ protected function formatDate($date, $idSite)
$timezone = Site::getTimezoneFor($idSite);
return Date::factory($date, $timezone)->getLocalized(Date::DATETIME_FORMAT_SHORT);
}

protected function getDraftContainerVersion(int $idSite, string $idContainer): int
{
$container = StaticContainer::get(Container::class)->getContainer($idSite, $idContainer);
if (empty($container)) {
throw new \Exception(Piwik::translate('TagManager_ErrorContainerDoesNotExist', [$idContainer]));
}

// Copy the new trigger to the draft version of the destination container
$idContainerVersion = $container['draft']['idcontainerversion'] ?? null;
// Make sure that the version is set and is an integer value
if (empty($idContainerVersion) || !(is_int($idContainerVersion) || (is_string($idContainerVersion) && ctype_digit($idContainerVersion)))) {
throw new \Exception(Piwik::translate('TagManager_ErrorContainerVersionDoesNotExist'));
}
// Make sure that the type is int
return intval($idContainerVersion);
}
}
15 changes: 1 addition & 14 deletions Model/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,20 +232,7 @@ public function copyTag(int $idSite, int $idContainerVersion, int $idTag, ?int $

private function copyReferencedVariablesAndTriggers(array &$tag, int $idSite, int $idContainerVersion, int $idDestinationSite, string $idDestinationContainer): int
{
$container = StaticContainer::get(Container::class);
$destinationContainer = $container->getContainer($idDestinationSite, $idDestinationContainer);
if (empty($destinationContainer)) {
throw new \Exception(Piwik::translate('TagManager_ErrorContainerDoesNotExist', [$idDestinationContainer]));
}

// Copy the new tag to the draft version of the destination container
$idDestinationVersion = $destinationContainer['draft']['idcontainerversion'] ?? null;
// Make sure that the version is set and is an integer value
if (empty($idDestinationVersion) || !(is_int($idDestinationVersion) || (is_string($idDestinationVersion) && ctype_digit($idDestinationVersion)))) {
throw new \Exception(Piwik::translate('TagManager_ErrorContainerVersionDoesNotExist'));
}
// Make sure that the type is int
$idDestinationVersion = intval($idDestinationVersion);
$idDestinationVersion = $this->getDraftContainerVersion($idDestinationSite, $idDestinationContainer);

// Copy all the referenced variables and triggers and replace those references with references to the newly copied ones
StaticContainer::get(Variable::class)->copyReferencedVariables($tag, $idSite, $idContainerVersion, $idDestinationSite, $idDestinationVersion);
Expand Down
34 changes: 34 additions & 0 deletions Model/Trigger.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,40 @@ public function copyTriggerIfNoEquivalent(int $idSite, int $idContainerVersion,
);
}

/**
* Make a copy of the trigger and return the ID.
*
* @param int $idSite
* @param int $idContainerVersion
* @param int $idTrigger
* @param null|int $idDestinationSite Optional ID of the site to which to copy the trigger. If empty, isSite is used
* @param string|null $idDestinationContainer Optional ID of the container to copy the trigger to. If not provided
* the copy goes to the source site and container
* @return int ID of the newly created trigger
*/
public function copyTrigger(int $idSite, int $idContainerVersion, int $idTrigger, ?int $idDestinationSite = 0, ?string $idDestinationContainer = null): int
{
$idDestinationSite = $idDestinationSite ?: $idSite;
$idDestinationVersion = $idContainerVersion;
if ($idDestinationSite !== null && !empty($idDestinationContainer)) {
$idDestinationVersion = $this->getDraftContainerVersion($idDestinationSite, $idDestinationContainer);
}

$trigger = $this->getContainerTrigger($idSite, $idContainerVersion, $idTrigger);
StaticContainer::get(Variable::class)->copyReferencedVariables($trigger, $idSite, $idContainerVersion, $idDestinationSite, $idDestinationVersion);

$newName = $this->dao->makeCopyNameUnique($idDestinationSite, $trigger['name'], $idDestinationVersion);
return $this->addContainerTrigger(
$idDestinationSite,
$idDestinationVersion,
$trigger['type'],
$newName,
$trigger['parameters'],
$trigger['conditions'],
$trigger['description']
);
}

private function updateTriggerColumns($idSite, $idContainerVersion, $idTrigger, $columns)
{
if (!isset($columns['updated_date'])) {
Expand Down
41 changes: 38 additions & 3 deletions Model/Variable.php
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,42 @@ public function copyReferencedVariables(array &$entity, int $idSite, int $idCont
}
}

/**
* Make a copy of the variable and return the ID.
*
* @param int $idSite
* @param int $idContainerVersion
* @param int $idVariable
* @param null|int $idDestinationSite Optional ID of the site to which to copy the variable. If empty, isSite is used
* @param string|null $idDestinationContainer Optional ID of the container to copy the variable to. If not provided
* the copy goes to the source site and container
* @return int ID of the newly created variable
*/
public function copyVariable(int $idSite, int $idContainerVersion, int $idVariable, ?int $idDestinationSite = 0, ?string $idDestinationContainer = null): int
{
$idDestinationSite = $idDestinationSite ?: $idSite;
$idDestinationVersion = $idContainerVersion;
if ($idDestinationSite !== null && !empty($idDestinationContainer)) {
$idDestinationVersion = $this->getDraftContainerVersion($idDestinationSite, $idDestinationContainer);
}

$variable = $this->getContainerVariable($idSite, $idContainerVersion, $idVariable);
$newVarName = $this->dao->makeCopyNameUnique($idDestinationSite, $variable['name'], $idDestinationVersion);

$this->copyReferencedVariables($variable, $idSite, $idContainerVersion, $idDestinationSite, $idDestinationVersion);

return $this->addContainerVariable(
$idDestinationSite,
$idDestinationVersion,
$variable['type'],
$newVarName,
$variable['parameters'],
$variable['default_value'],
$variable['lookup_table'],
$variable['description']
);
}

private function copyVariableByNameIfNoEquivalent(string $variableName, int $idSite, int $idContainerVersion, int $idDestinationSite, int $idDestinationContainerVersion): string
{
$variable = $this->findVariableByName($idSite, $idContainerVersion, $variableName);
Expand Down Expand Up @@ -483,11 +519,10 @@ private function copyVariableByName(array $variable, int $idSite, int $idContain
throw new \Exception('Variable name cannot be empty');
}

$variableName = $variable['name'];

$this->copyReferencedVariables($variable, $idSite, $idContainerVersion, $idDestinationSite, $idDestinationContainerVersion);

// Insert the new variable
$newVarName = $this->dao->makeCopyNameUnique($idDestinationSite, $variableName, $idDestinationContainerVersion);
$newVarName = $this->dao->makeCopyNameUnique($idDestinationSite, $variable['name'], $idDestinationContainerVersion);
$this->addContainerVariable(
$idDestinationSite,
$idDestinationContainerVersion,
Expand Down
10 changes: 10 additions & 0 deletions stylesheets/manageList.less
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
}
}

.tagManagerTriggerList {
td.action.hasCopyAction {
width: 170px;
}
}

.tagManagerContainerList {
th.action, td.action {
width: 240px;
Expand All @@ -79,6 +85,10 @@
display: inline-block;
color: #999;
}

td.action.hasCopyAction {
width: 170px;
}
}

.page:after {
Expand Down
Loading