diff --git a/core/Plugin/Segment.php b/core/Plugin/Segment.php index 3d6914fe5cb91f62fe7d25073e23d9a17633b421..2a20208ea28f050e9304a4c2dbdfef59a7373dba 100644 --- a/core/Plugin/Segment.php +++ b/core/Plugin/Segment.php @@ -7,6 +7,7 @@ * */ namespace Piwik\Plugin; +use Exception; /** * Creates a new segment that can be used for instance within the {@link \Piwik\Columns\Dimension::configureSegment()} @@ -123,6 +124,7 @@ class Segment public function setSegment($segment) { $this->segment = $segment; + $this->check(); } /** @@ -166,6 +168,7 @@ class Segment public function setSqlSegment($sqlSegment) { $this->sqlSegment = $sqlSegment; + $this->check(); } /** @@ -178,6 +181,7 @@ class Segment public function setUnionOfSegments($segments) { $this->unionOfSegments = $segments; + $this->check(); } /** @@ -323,4 +327,15 @@ class Segment { $this->requiresAtLeastViewAccess = $requiresAtLeastViewAccess; } + + private function check() + { + if ($this->sqlSegment && $this->unionOfSegments) { + throw new Exception(sprintf('Union of segments and SQL segment is set for segment "%s", use only one of them', $this->name)); + } + + if ($this->segment && $this->unionOfSegments && in_array($this->segment, $this->unionOfSegments, true)) { + throw new Exception(sprintf('The segment %s contains a union segment to itself', $this->name)); + } + } } diff --git a/lang/en.json b/lang/en.json index 615ad706c093bd79edcec361073c5a71f7ec7fb4..c5a32ed0d8a2401d6ca27ec19ae5668c7c809a95 100644 --- a/lang/en.json +++ b/lang/en.json @@ -264,6 +264,8 @@ "OperationIsNot": "Is not", "OperationLessThan": "Less than", "OperationNotEquals": "Not Equals", + "OperationStartsWith": "Starts with", + "OperationEndsWith": "Ends with", "OptionalSmtpPort": "Optional. Defaults to 25 for unencrypted and TLS SMTP, and 465 for SSL SMTP.", "Options": "Options", "OrCancel": "or %s Cancel %s", diff --git a/plugins/API/API.php b/plugins/API/API.php index 09e47198240bc52a4d2ef44ab44f20968e3f79e8..e5d9fbcd2a56848ca3c659f223d79ae83ca8b9b3 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -129,14 +129,6 @@ class API extends \Piwik\Plugin\API $segment->setPermission($isAuthenticatedWithViewAccess); } - if ($segment->getSqlSegment() && $segment->getUnionOfSegments()) { - throw new \Exception(sprintf('Union of segments and SQL segment is set for segment "%s", use only one of them', $segment->getName())); - } - - if ($segment->getUnionOfSegments() && in_array($segment->getSegment(), $segment->getUnionOfSegments(), true)) { - throw new \Exception(sprintf('The segment %s contains a union segment to itself', $segment->getName())); - } - $segments[] = $segment->toArray(); } } diff --git a/plugins/SegmentEditor/SegmentSelectorControl.php b/plugins/SegmentEditor/SegmentSelectorControl.php index e37354e1cd8a0ac81f82d9012c98cc91357ebaa7..9dc2659660f9bf1da55d953b23d6c1d96dda0a1e 100644 --- a/plugins/SegmentEditor/SegmentSelectorControl.php +++ b/plugins/SegmentEditor/SegmentSelectorControl.php @@ -115,6 +115,8 @@ class SegmentSelectorControl extends UIControl 'General_OperationGreaterThan', 'General_OperationContains', 'General_OperationDoesNotContain', + 'General_OperationStartsWith', + 'General_OperationEndsWith', 'General_OperationIs', 'General_OperationIsNot', 'General_OperationContains', diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index 0e47bd7d195f1e8e58fd0928a8dce8ed9fc2b4ad..ec1e8f23b8ca239835fefbf9d21b322e4cf9c00e 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -49,6 +49,8 @@ Segmentation = (function($) { self.availableMatches["dimension"]["!="] = self.translations['General_OperationIsNot']; self.availableMatches["dimension"]["=@"] = self.translations['General_OperationContains']; self.availableMatches["dimension"]["!@"] = self.translations['General_OperationDoesNotContain']; + self.availableMatches["dimension"]["=^"] = self.translations['General_OperationStartsWith']; + self.availableMatches["dimension"]["=$"] = self.translations['General_OperationEndsWith']; segmentation.prototype.setAvailableSegments = function (segments) { this.availableSegments = segments; @@ -264,7 +266,7 @@ Segmentation = (function($) { }; var findAndExplodeByMatch = function(metric){ - var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">"]; + var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">", "=^", "=$"]; var newMetric = {}; var minPos = metric.length; var match, index; diff --git a/plugins/SegmentEditor/templates/_segmentSelector.twig b/plugins/SegmentEditor/templates/_segmentSelector.twig index 1ffb4b3d472b56326979877af9ff7f7aca4442a6..f2d53b60c70a6a1089d2018fd879f157260569d4 100644 --- a/plugins/SegmentEditor/templates/_segmentSelector.twig +++ b/plugins/SegmentEditor/templates/_segmentSelector.twig @@ -61,6 +61,8 @@ <option value=">">{{ 'General_OperationGreaterThan'|translate }}</option> <option value="=@">{{ 'General_OperationContains'|translate }}</option> <option value="!@">{{ 'General_OperationDoesNotContain'|translate }}</option> + <option value="=^">{{ 'General_OperationStartsWith'|translate }}</option> + <option value="=$">{{ 'General_OperationEndsWith'|translate }}</option> </select> </div> <div class="segment-input metricValueBlock">