PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.0.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.0.2
5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Segment.php
matomo / app / core Last commit date
API 5 years ago Access 5 years ago Application 5 years ago Archive 5 years ago ArchiveProcessor 5 years ago Archiver 5 years ago AssetManager 5 years ago Auth 5 years ago Category 5 years ago CliMulti 5 years ago Columns 5 years ago Composer 5 years ago Concurrency 5 years ago Config 5 years ago Container 5 years ago CronArchive 5 years ago DataAccess 5 years ago DataFiles 5 years ago DataTable 5 years ago Db 5 years ago DeviceDetector 5 years ago Email 5 years ago Exception 5 years ago Http 5 years ago Intl 5 years ago Mail 5 years ago Measurable 5 years ago Menu 5 years ago Metrics 5 years ago Notification 5 years ago Period 5 years ago Plugin 5 years ago ProfessionalServices 5 years ago Report 5 years ago ReportRenderer 5 years ago Scheduler 5 years ago Segment 5 years ago Session 5 years ago Settings 5 years ago Tracker 5 years ago Translation 5 years ago UpdateCheck 5 years ago Updater 5 years ago Updates 5 years ago Validators 5 years ago View 5 years ago ViewDataTable 5 years ago Visualization 5 years ago Widget 5 years ago .htaccess 6 years ago Access.php 5 years ago Archive.php 5 years ago ArchiveProcessor.php 5 years ago AssetManager.php 5 years ago Auth.php 5 years ago AuthResult.php 5 years ago BaseFactory.php 5 years ago Cache.php 5 years ago CacheId.php 5 years ago CliMulti.php 5 years ago Common.php 5 years ago Config.php 5 years ago Console.php 5 years ago Context.php 5 years ago Cookie.php 5 years ago CronArchive.php 5 years ago DataArray.php 5 years ago DataTable.php 5 years ago Date.php 5 years ago Db.php 5 years ago DbHelper.php 5 years ago Development.php 5 years ago ErrorHandler.php 5 years ago EventDispatcher.php 5 years ago ExceptionHandler.php 5 years ago FileIntegrity.php 5 years ago Filechecks.php 5 years ago Filesystem.php 5 years ago FrontController.php 5 years ago Http.php 5 years ago IP.php 5 years ago Log.php 5 years ago LogDeleter.php 5 years ago Mail.php 5 years ago Metrics.php 5 years ago NoAccessException.php 5 years ago Nonce.php 5 years ago Notification.php 5 years ago NumberFormatter.php 5 years ago Option.php 5 years ago Period.php 5 years ago Piwik.php 5 years ago Plugin.php 5 years ago Profiler.php 5 years ago ProxyHeaders.php 5 years ago ProxyHttp.php 5 years ago QuickForm2.php 5 years ago RankingQuery.php 5 years ago ReportRenderer.php 5 years ago Segment.php 5 years ago Sequence.php 5 years ago Session.php 5 years ago SettingsPiwik.php 5 years ago SettingsServer.php 5 years ago Singleton.php 5 years ago Site.php 5 years ago TCPDF.php 5 years ago Theme.php 5 years ago Timer.php 5 years ago Tracker.php 5 years ago Twig.php 5 years ago Unzip.php 5 years ago UpdateCheck.php 5 years ago Updater.php 5 years ago UpdaterErrorException.php 5 years ago Updates.php 5 years ago Url.php 5 years ago UrlHelper.php 5 years ago Version.php 5 years ago View.php 5 years ago bootstrap.php 5 years ago dispatch.php 5 years ago testMinimumPhpVersion.php 5 years ago
Segment.php
604 lines
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 */
9 namespace Piwik;
10
11 use Exception;
12 use Piwik\API\Request;
13 use Piwik\ArchiveProcessor\Rules;
14 use Piwik\Container\StaticContainer;
15 use Piwik\DataAccess\LogQueryBuilder;
16 use Piwik\Plugins\SegmentEditor\SegmentEditor;
17 use Piwik\Segment\SegmentExpression;
18
19 /**
20 * Limits the set of visits Piwik uses when aggregating analytics data.
21 *
22 * A segment is a condition used to filter visits. They can, for example,
23 * select visits that have a specific browser or come from a specific
24 * country, or both.
25 *
26 * Plugins that aggregate data stored in Piwik can support segments by
27 * using this class when generating aggregation SQL queries.
28 *
29 * ### Examples
30 *
31 * **Basic usage**
32 *
33 * $idSites = array(1,2,3);
34 * $segmentStr = "browserCode==ff;countryCode==CA";
35 * $segment = new Segment($segmentStr, $idSites);
36 *
37 * $query = $segment->getSelectQuery(
38 * $select = "table.col1, table2.col2",
39 * $from = array("table", "table2"),
40 * $where = "table.col3 = ?",
41 * $bind = array(5),
42 * $orderBy = "table.col1 DESC",
43 * $groupBy = "table2.col2"
44 * );
45 *
46 * Db::fetchAll($query['sql'], $query['bind']);
47 *
48 * **Creating a _null_ segment**
49 *
50 * $idSites = array(1,2,3);
51 * $segment = new Segment('', $idSites);
52 * // $segment->getSelectQuery will return a query that selects all visits
53 *
54 * @api
55 */
56 class Segment
57 {
58 /**
59 * @var SegmentExpression
60 */
61 protected $segmentExpression = null;
62
63 /**
64 * @var string
65 */
66 protected $string = null;
67
68 /**
69 * @var string
70 */
71 protected $originalString = null;
72
73 /**
74 * @var array
75 */
76 protected $idSites = null;
77
78 /**
79 * @var Date
80 */
81 protected $startDate = null;
82
83 /**
84 * @var Date
85 */
86 protected $endDate = null;
87
88 /**
89 * @var LogQueryBuilder
90 */
91 private $segmentQueryBuilder;
92
93 /**
94 * @var bool
95 */
96 private $isSegmentEncoded;
97
98 /**
99 * Truncate the Segments to 8k
100 */
101 const SEGMENT_TRUNCATE_LIMIT = 8192;
102
103 /**
104 * Constructor.
105 *
106 * When using segments that contain a != or !@ condition on a non visit dimension (e.g. action, conversion, ...) it
107 * is needed to use a subquery to get correct results. To avoid subqueries that fetch too many data it's required to
108 * set a startDate and/or an endDate in this case. That date will be used to limit the subquery (along with possibly
109 * given idSites). If no startDate and endDate is given for such a segment it will generate a query that directly
110 * joins the according tables, but trigger a php warning as results might be incorrect.
111 *
112 * @param string $segmentCondition The segment condition, eg, `'browserCode=ff;countryCode=CA'`.
113 * @param array $idSites The list of sites the segment will be used with. Some segments are
114 * dependent on the site, such as goal segments.
115 * @param Date|null $startDate start date used to limit subqueries
116 * @param Date|null $endDate end date used to limit subqueries
117 * @throws
118 */
119 public function __construct($segmentCondition, $idSites, Date $startDate = null, Date $endDate = null)
120 {
121 $this->segmentQueryBuilder = StaticContainer::get('Piwik\DataAccess\LogQueryBuilder');
122
123 $segmentCondition = trim($segmentCondition);
124 if (!SettingsPiwik::isSegmentationEnabled()
125 && !empty($segmentCondition)
126 ) {
127 throw new Exception("The Super User has disabled the Segmentation feature.");
128 }
129
130 $this->originalString = $segmentCondition;
131
132 if ($startDate instanceof Date) {
133 $this->startDate = $startDate;
134 }
135
136 if ($endDate instanceof Date) {
137 $this->endDate = $endDate;
138 }
139
140 // The segment expression can be urlencoded. Unfortunately, both the encoded and decoded versions
141 // can usually be parsed successfully. To pick the right one, we try both and pick the one w/ more
142 // successfully parsed subexpressions.
143 $subexpressionsDecoded = 0;
144 try {
145 $this->initializeSegment(urldecode($segmentCondition), $idSites);
146 $subexpressionsDecoded = $this->segmentExpression->getSubExpressionCount();
147 } catch (Exception $e) {
148 // ignore
149 }
150
151 $subexpressionsRaw = 0;
152 try {
153 $this->initializeSegment($segmentCondition, $idSites);
154 $subexpressionsRaw = $this->segmentExpression->getSubExpressionCount();
155 } catch (Exception $e) {
156 // ignore
157 }
158
159 if ($subexpressionsRaw > $subexpressionsDecoded) {
160 $this->initializeSegment($segmentCondition, $idSites);
161 $this->isSegmentEncoded = false;
162 } else {
163 $this->initializeSegment(urldecode($segmentCondition), $idSites);
164 $this->isSegmentEncoded = true;
165 }
166 }
167
168 /**
169 * Returns the segment expression.
170 * @return SegmentExpression
171 * @api since Piwik 3.2.0
172 */
173 public function getSegmentExpression()
174 {
175 return $this->segmentExpression;
176 }
177
178 private function getAvailableSegments()
179 {
180 // segment metadata
181 if (empty($this->availableSegments)) {
182 $this->availableSegments = Request::processRequest('API.getSegmentsMetadata', array(
183 'idSites' => $this->idSites,
184 '_hideImplementationData' => 0,
185 'filter_limit' => -1,
186 'filter_offset' => 0,
187 '_showAllSegments' => 1,
188 ), []);
189 }
190
191 return $this->availableSegments;
192 }
193
194 private function getSegmentByName($name)
195 {
196 $segments = $this->getAvailableSegments();
197
198 foreach ($segments as $segment) {
199 if ($segment['segment'] == $name && !empty($name)) {
200
201 // check permission
202 if (isset($segment['permission']) && $segment['permission'] != 1) {
203 throw new NoAccessException("You do not have enough permission to access the segment " . $name);
204 }
205
206 return $segment;
207 }
208 }
209
210 throw new Exception("Segment '$name' is not a supported segment.");
211 }
212
213 /**
214 * @param $string
215 * @param $idSites
216 * @throws Exception
217 */
218 protected function initializeSegment($string, $idSites)
219 {
220 // As a preventive measure, we restrict the filter size to a safe limit
221 $string = substr($string, 0, self::SEGMENT_TRUNCATE_LIMIT);
222
223 $this->string = $string;
224
225 if (empty($idSites)) {
226 $idSites = [];
227 } else if (!is_array($idSites)) {
228 $idSites = [$idSites];
229 }
230 $this->idSites = $idSites;
231 $segment = new SegmentExpression($string);
232 $this->segmentExpression = $segment;
233
234 // parse segments
235 $expressions = $segment->parseSubExpressions();
236 $expressions = $this->getExpressionsWithUnionsResolved($expressions);
237
238 // convert segments name to sql segment
239 // check that user is allowed to view this segment
240 // and apply a filter to the value to match if necessary (to map DB fields format)
241 $cleanedExpressions = array();
242 foreach ($expressions as $expression) {
243 $operand = $expression[SegmentExpression::INDEX_OPERAND];
244 $expression[SegmentExpression::INDEX_OPERAND] = $this->getCleanedExpression($operand);
245 $cleanedExpressions[] = $expression;
246 }
247
248 $segment->setSubExpressionsAfterCleanup($cleanedExpressions);
249 }
250
251 private function getExpressionsWithUnionsResolved($expressions)
252 {
253 $expressionsWithUnions = array();
254 foreach ($expressions as $expression) {
255 $operand = $expression[SegmentExpression::INDEX_OPERAND];
256 $name = $operand[SegmentExpression::INDEX_OPERAND_NAME];
257
258 $availableSegment = $this->getSegmentByName($name);
259
260 // We leave segments using !@ and != operands untouched for segments not on log_visit table as they will be build using a subquery
261 if (!$this->doesSegmentNeedSubquery($operand[SegmentExpression::INDEX_OPERAND_OPERATOR], $name) && !empty($availableSegment['unionOfSegments'])) {
262 $count = 0;
263 foreach ($availableSegment['unionOfSegments'] as $segmentNameOfUnion) {
264 $count++;
265 $operator = SegmentExpression::BOOL_OPERATOR_OR; // we connect all segments within that union via OR
266 if ($count === count($availableSegment['unionOfSegments'])) {
267 $operator = $expression[SegmentExpression::INDEX_BOOL_OPERATOR];
268 }
269
270 $operand[SegmentExpression::INDEX_OPERAND_NAME] = $segmentNameOfUnion;
271 $expressionsWithUnions[] = array(
272 SegmentExpression::INDEX_BOOL_OPERATOR => $operator,
273 SegmentExpression::INDEX_OPERAND => $operand
274 );
275 }
276 } else {
277 $expressionsWithUnions[] = array(
278 SegmentExpression::INDEX_BOOL_OPERATOR => $expression[SegmentExpression::INDEX_BOOL_OPERATOR],
279 SegmentExpression::INDEX_OPERAND => $operand
280 );
281 }
282 }
283
284 return $expressionsWithUnions;
285 }
286
287 private function isVisitSegment($name)
288 {
289 $availableSegment = $this->getSegmentByName($name);
290
291 if (!empty($availableSegment['unionOfSegments'])) {
292 foreach ($availableSegment['unionOfSegments'] as $segmentNameOfUnion) {
293 $unionSegment = $this->getSegmentByName($segmentNameOfUnion);
294 if (strpos($unionSegment['sqlSegment'], 'log_visit.') === 0) {
295 return true;
296 }
297 }
298 } else if (strpos($availableSegment['sqlSegment'], 'log_visit.') === 0) {
299 return true;
300 }
301
302 return false;
303 }
304
305 private function doesSegmentNeedSubquery($operator, $segmentName)
306 {
307 $requiresSubQuery = in_array($operator, [
308 SegmentExpression::MATCH_DOES_NOT_CONTAIN,
309 SegmentExpression::MATCH_NOT_EQUAL
310 ]) && !$this->isVisitSegment($segmentName);
311
312 if ($requiresSubQuery && empty($this->startDate) && empty($this->endDate)) {
313 if (Development::isEnabled()) {
314 $e = new Exception();
315 Log::warning("Avoiding segment subquery due to missing start date and/or an end date. Please ensure a start date and/or end date is set when initializing a segment if it's used to build a query. Stacktrace:\n" . $e->getTraceAsString());
316 }
317 return false;
318 }
319
320 return $requiresSubQuery;
321 }
322
323 private function getInvertedOperatorForSubQuery($operator)
324 {
325 if ($operator === SegmentExpression::MATCH_DOES_NOT_CONTAIN) {
326 return SegmentExpression::MATCH_CONTAINS;
327 } else if ($operator === SegmentExpression::MATCH_NOT_EQUAL) {
328 return SegmentExpression::MATCH_EQUAL;
329 }
330
331 throw new Exception("Operator not support for subqueries");
332 }
333
334 /**
335 * Returns `true` if the segment is empty, `false` if otherwise.
336 */
337 public function isEmpty()
338 {
339 return $this->segmentExpression->isEmpty();
340 }
341
342 /**
343 * Detects whether the Piwik instance is configured to be able to archive this segment. It checks whether the segment
344 * will be either archived via browser or cli archiving. It does not check if the segment has been archived. If you
345 * want to know whether the segment has been archived, the actual report data needs to be requested.
346 *
347 * This method does not take any date/period into consideration. Meaning a Piwik instance might be able to archive
348 * this segment in general, but not for a certain period if eg the archiving of range dates is disabled.
349 *
350 * @return bool
351 */
352 public function willBeArchived()
353 {
354 if ($this->isEmpty()) {
355 return true;
356 }
357
358 $idSites = $this->idSites;
359
360 return Rules::isRequestAuthorizedToArchive()
361 || Rules::isBrowserArchivingAvailableForSegments()
362 || Rules::isSegmentPreProcessed($idSites, $this);
363 }
364
365 protected $availableSegments = array();
366
367 protected function getCleanedExpression($expression)
368 {
369 $name = $expression[SegmentExpression::INDEX_OPERAND_NAME];
370 $matchType = $expression[SegmentExpression::INDEX_OPERAND_OPERATOR];
371 $value = $expression[SegmentExpression::INDEX_OPERAND_VALUE];
372
373 $segment = $this->getSegmentByName($name);
374 $sqlName = $segment['sqlSegment'];
375
376 // Build subqueries for segments that are not on log_visit table but use !@ or != as operator
377 // This is required to ensure segments like actionUrl!@value really do not include any visit having an action containing `value`
378 if ($this->doesSegmentNeedSubquery($matchType, $name)) {
379 $operator = $this->getInvertedOperatorForSubQuery($matchType);
380 $stringSegment = $name . $operator . $value;
381 $segmentObj = new Segment($stringSegment, $this->idSites, $this->startDate, $this->endDate);
382
383 $select = 'log_visit.idvisit';
384 $from = 'log_visit';
385 $datetimeField = 'visit_last_action_time';
386 $where = [];
387 $bind = [];
388 if (!empty($this->idSites)) {
389 $where[] = "$from.idsite IN (" . Common::getSqlStringFieldsArray($this->idSites) . ")";
390 $bind = $this->idSites;
391 }
392 if ($this->startDate instanceof Date) {
393 $where[] = "$from.$datetimeField >= ?";
394 $bind[] = $this->startDate->toString(Date::DATE_TIME_FORMAT);
395 }
396 if ($this->endDate instanceof Date) {
397 $where[] = "$from.$datetimeField <= ?";
398 $bind[] = $this->endDate->toString(Date::DATE_TIME_FORMAT);
399 }
400
401 $logQueryBuilder = StaticContainer::get('Piwik\DataAccess\LogQueryBuilder');
402 $forceGroupByBackup = $logQueryBuilder->getForcedInnerGroupBySubselect();
403 $logQueryBuilder->forceInnerGroupBySubselect(LogQueryBuilder::FORCE_INNER_GROUP_BY_NO_SUBSELECT);
404 $query = $segmentObj->getSelectQuery($select, $from, implode(' AND ', $where), $bind);
405 $logQueryBuilder->forceInnerGroupBySubselect($forceGroupByBackup);
406
407 return ['log_visit.idvisit', SegmentExpression::MATCH_ACTIONS_NOT_CONTAINS, $query];
408 }
409
410 if ($matchType != SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY
411 && $matchType != SegmentExpression::MATCH_IS_NULL_OR_EMPTY) {
412
413 if (isset($segment['sqlFilterValue'])) {
414 $value = call_user_func($segment['sqlFilterValue'], $value, $segment['sqlSegment']);
415 }
416
417 // apply presentation filter
418 if (isset($segment['sqlFilter'])) {
419 $value = call_user_func($segment['sqlFilter'], $value, $segment['sqlSegment'], $matchType, $name);
420
421 if(is_null($value)) { // null is returned in TableLogAction::getIdActionFromSegment()
422 return array(null, $matchType, null);
423 }
424
425 // sqlFilter-callbacks might return arrays for more complex cases
426 // e.g. see TableLogAction::getIdActionFromSegment()
427 if (is_array($value) && isset($value['SQL'])) {
428 // Special case: returned value is a sub sql expression!
429 $matchType = SegmentExpression::MATCH_ACTIONS_CONTAINS;
430 }
431 }
432 }
433
434 return array($sqlName, $matchType, $value);
435 }
436
437 /**
438 * Returns the segment condition.
439 *
440 * @return string
441 */
442 public function getString()
443 {
444 return $this->string;
445 }
446
447 /**
448 * Returns a hash of the segment condition, or the empty string if the segment
449 * condition is empty.
450 *
451 * @return string
452 */
453 public function getHash()
454 {
455 if (empty($this->string)) {
456 return '';
457 }
458 return self::getSegmentHash($this->string);
459 }
460
461 public static function getSegmentHash($definition)
462 {
463 // urldecode to normalize the string, as browsers may send slightly different payloads for the same archive
464 return md5(urldecode($definition));
465 }
466
467 /**
468 * Extend an SQL query that aggregates data over one of the 'log_' tables with segment expressions.
469 *
470 * @param string $select The select clause. Should NOT include the **SELECT** just the columns, eg,
471 * `'t1.col1 as col1, t2.col2 as col2'`.
472 * @param array|string $from Array of table names (without prefix), eg, `array('log_visit', 'log_conversion')`.
473 * @param false|string $where (optional) Where clause, eg, `'t1.col1 = ? AND t2.col2 = ?'`.
474 * @param array|string $bind (optional) Bind parameters, eg, `array($col1Value, $col2Value)`.
475 * @param false|string $orderBy (optional) Order by clause, eg, `"t1.col1 ASC"`.
476 * @param false|string $groupBy (optional) Group by clause, eg, `"t2.col2"`.
477 * @param int $limit Limit number of result to $limit
478 * @param int $offset Specified the offset of the first row to return
479 * @param bool $forceGroupBy Force the group by and not using a subquery. Note: This may make the query slower see https://github.com/matomo-org/matomo/issues/9200#issuecomment-183641293
480 * A $groupBy value needs to be set for this to work.
481 * @param int If set to value >= 1 then the Select query (and All inner queries) will be LIMIT'ed by this value.
482 * Use only when you're not aggregating or it will sample the data.
483 * @return string The entire select query.
484 */
485 public function getSelectQuery($select, $from, $where = false, $bind = array(), $orderBy = false, $groupBy = false, $limit = 0, $offset = 0, $forceGroupBy = false)
486 {
487 $segmentExpression = $this->segmentExpression;
488
489 $limitAndOffset = null;
490 if($limit > 0) {
491 $limitAndOffset = (int) $offset . ', ' . (int) $limit;
492 }
493
494 try {
495 if ($forceGroupBy && $groupBy) {
496 $this->segmentQueryBuilder->forceInnerGroupBySubselect(LogQueryBuilder::FORCE_INNER_GROUP_BY_NO_SUBSELECT);
497 }
498 $result = $this->segmentQueryBuilder->getSelectQueryString($segmentExpression, $select, $from, $where, $bind,
499 $groupBy, $orderBy, $limitAndOffset);
500 } catch (Exception $e) {
501 if ($forceGroupBy && $groupBy) {
502 $this->segmentQueryBuilder->forceInnerGroupBySubselect('');
503 }
504 throw $e;
505 }
506
507 if ($forceGroupBy && $groupBy) {
508 $this->segmentQueryBuilder->forceInnerGroupBySubselect('');
509 }
510 return $result;
511 }
512
513 /**
514 * Returns the segment string.
515 *
516 * @return string
517 */
518 public function __toString()
519 {
520 return (string) $this->getString();
521 }
522
523 /**
524 * Combines this segment with another segment condition, if the segment condition is not already
525 * in the segment.
526 *
527 * The combination is naive in that it does not take order of operations into account.
528 *
529 * @param string $segment
530 * @param string $operator The operator to use. Should be either SegmentExpression::AND_DELIMITER
531 * or SegmentExpression::OR_DELIMITER.
532 * @param string $segmentCondition The segment condition to add.
533 * @return string
534 * @throws Exception
535 */
536 public static function combine($segment, $operator, $segmentCondition)
537 {
538 if (empty($segment)) {
539 return $segmentCondition;
540 }
541
542 if (empty($segmentCondition)
543 || self::containsCondition($segment, $operator, $segmentCondition)
544 ) {
545 return $segment;
546 }
547
548 return $segment . $operator . $segmentCondition;
549 }
550
551 private static function containsCondition($segment, $operator, $segmentCondition)
552 {
553 // check when segment/condition are of same encoding
554 return strpos($segment, $operator . $segmentCondition) !== false
555 || strpos($segment, $segmentCondition . $operator) !== false
556
557 // check when both operator & condition are urlencoded in $segment
558 || strpos($segment, urlencode($operator . $segmentCondition)) !== false
559 || strpos($segment, urlencode($segmentCondition . $operator)) !== false
560
561 // check when operator is not urlencoded, but condition is in $segment
562 || strpos($segment, $operator . urlencode($segmentCondition)) !== false
563 || strpos($segment, urlencode($segmentCondition) . $operator) !== false
564
565 // check when segment condition is urlencoded & $segment isn't
566 || strpos($segment, $operator . urldecode($segmentCondition)) !== false
567 || strpos($segment, urldecode($segmentCondition) . $operator) !== false
568
569 || $segment === $segmentCondition
570 || $segment === urlencode($segmentCondition)
571 || $segment === urldecode($segmentCondition);
572 }
573
574 public function getStoredSegmentName($idSite)
575 {
576 $segment = $this->getString();
577 if (empty($segment)) {
578 return Piwik::translate('SegmentEditor_DefaultAllVisits');
579 }
580
581 $availableSegments = SegmentEditor::getAllSegmentsForSite($idSite);
582
583 $foundStoredSegment = null;
584 foreach ($availableSegments as $storedSegment) {
585 if ($storedSegment['definition'] == $segment
586 || $storedSegment['definition'] == urldecode($segment)
587 || $storedSegment['definition'] == urlencode($segment)
588
589 || $storedSegment['definition'] == $this->originalString
590 || $storedSegment['definition'] == urldecode($this->originalString)
591 || $storedSegment['definition'] == urlencode($this->originalString)
592 ) {
593 $foundStoredSegment = $storedSegment;
594 }
595 }
596
597 if (isset($foundStoredSegment)) {
598 return $foundStoredSegment['name'];
599 }
600
601 return $this->isSegmentEncoded ? urldecode($segment) : $segment;
602 }
603 }
604