PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
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 / Tracker / Action.php
matomo / app / core / Tracker Last commit date
Db 6 years ago Handler 6 years ago TableLogAction 6 years ago Visit 6 years ago Action.php 6 years ago ActionPageview.php 6 years ago Cache.php 6 years ago Db.php 6 years ago Failures.php 6 years ago FingerprintSalt.php 6 years ago GoalManager.php 6 years ago Handler.php 6 years ago IgnoreCookie.php 6 years ago LogTable.php 6 years ago Model.php 6 years ago PageUrl.php 6 years ago Request.php 5 years ago RequestProcessor.php 6 years ago RequestSet.php 6 years ago Response.php 6 years ago ScheduledTasksRunner.php 6 years ago Settings.php 5 years ago TableLogAction.php 6 years ago TrackerCodeGenerator.php 6 years ago TrackerConfig.php 6 years ago Visit.php 5 years ago VisitExcluded.php 6 years ago VisitInterface.php 6 years ago Visitor.php 6 years ago VisitorNotFoundInDb.php 6 years ago VisitorRecognizer.php 6 years ago
Action.php
455 lines
1 <?php
2 /**
3 * Piwik - 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
10 namespace Piwik\Tracker;
11
12 use Exception;
13 use Piwik\Common;
14 use Piwik\Container\StaticContainer;
15 use Piwik\Piwik;
16 use Piwik\Plugin\Dimension\ActionDimension;
17 use Piwik\Plugin\Manager;
18 use Piwik\Tracker;
19 use Psr\Log\LoggerInterface;
20
21 /**
22 * An action
23 *
24 */
25 abstract class Action
26 {
27 const TYPE_PAGE_URL = 1;
28 const TYPE_OUTLINK = 2;
29 const TYPE_DOWNLOAD = 3;
30 const TYPE_PAGE_TITLE = 4;
31 const TYPE_ECOMMERCE_ITEM_SKU = 5;
32 const TYPE_ECOMMERCE_ITEM_NAME = 6;
33 const TYPE_ECOMMERCE_ITEM_CATEGORY = 7;
34 const TYPE_SITE_SEARCH = 8;
35
36 const TYPE_EVENT = 10; // Alias TYPE_EVENT_CATEGORY
37 const TYPE_EVENT_CATEGORY = 10;
38 const TYPE_EVENT_ACTION = 11;
39 const TYPE_EVENT_NAME = 12;
40
41 const TYPE_CONTENT = 13; // Alias TYPE_CONTENT_NAME
42 const TYPE_CONTENT_NAME = 13;
43 const TYPE_CONTENT_PIECE = 14;
44 const TYPE_CONTENT_TARGET = 15;
45 const TYPE_CONTENT_INTERACTION = 16;
46
47 const DB_COLUMN_CUSTOM_FLOAT = 'custom_float';
48
49 private static $factoryPriority = array(
50 self::TYPE_PAGE_URL,
51 self::TYPE_SITE_SEARCH,
52 self::TYPE_CONTENT,
53 self::TYPE_EVENT,
54 self::TYPE_OUTLINK,
55 self::TYPE_DOWNLOAD
56 );
57
58 /**
59 * Public so that events listener can access it
60 *
61 * @var Request
62 */
63 public $request;
64
65 private $idLinkVisitAction;
66 private $actionIdsCached = array();
67 private $customFields = array();
68 private $actionName;
69 private $actionType;
70
71 /**
72 * URL with excluded Query parameters
73 */
74 private $actionUrl;
75
76 /**
77 * Raw URL (will contain excluded URL query parameters)
78 */
79 private $rawActionUrl;
80
81 /**
82 * @var mixed|LoggerInterface
83 */
84 private $logger;
85
86 /**
87 * Makes the correct Action object based on the request.
88 *
89 * @param Request $request
90 * @return Action
91 */
92 public static function factory(Request $request)
93 {
94 /** @var Action[] $actions */
95 $actions = self::getAllActions($request);
96
97 foreach ($actions as $actionType) {
98 if (empty($action)) {
99 $action = $actionType;
100 continue;
101 }
102
103 $posPrevious = self::getPriority($action);
104 $posCurrent = self::getPriority($actionType);
105
106 if ($posCurrent > $posPrevious) {
107 $action = $actionType;
108 }
109 }
110
111 if (!empty($action)) {
112 return $action;
113 }
114
115 return new ActionPageview($request);
116 }
117
118 private static function getPriority(Action $actionType)
119 {
120 $key = array_search($actionType->getActionType(), self::$factoryPriority);
121
122 if (false === $key) {
123 return -1;
124 }
125
126 return $key;
127 }
128
129 public static function shouldHandle(Request $request)
130 {
131 return false;
132 }
133
134 private static function getAllActions(Request $request)
135 {
136 static $actions;
137
138 if (is_null($actions)) {
139 $actions = Manager::getInstance()->findMultipleComponents('Actions', '\\Piwik\\Tracker\\Action');
140 }
141
142 $instances = array();
143
144 foreach ($actions as $action) {
145 /** @var \Piwik\Tracker\Action $action */
146 if ($action::shouldHandle($request)) {
147 $instances[] = new $action($request);
148 }
149 }
150
151 return $instances;
152 }
153
154 public function __construct($type, Request $request)
155 {
156 $this->actionType = $type;
157 $this->request = $request;
158 $this->logger = StaticContainer::get(LoggerInterface::class);
159 }
160
161 /**
162 * Returns URL of the page currently being tracked, or the file being downloaded, or the outlink being clicked
163 *
164 * @return string
165 */
166 public function getActionUrl()
167 {
168 return $this->actionUrl;
169 }
170
171 /**
172 * Returns URL of page being tracked, including all original Query parameters
173 */
174 public function getActionUrlRaw()
175 {
176 return $this->rawActionUrl;
177 }
178
179 public function getActionName()
180 {
181 return $this->actionName;
182 }
183
184 public function getActionType()
185 {
186 return $this->actionType;
187 }
188
189 public function getCustomVariables()
190 {
191 return $this->request->getCustomVariables($scope = 'page');
192 }
193
194 // custom_float column
195 public function getCustomFloatValue()
196 {
197 return false;
198 }
199
200 protected function setActionName($name)
201 {
202 $this->actionName = PageUrl::cleanupString((string)$name);
203 }
204
205 protected function setActionUrl($url)
206 {
207 $this->rawActionUrl = PageUrl::getUrlIfLookValid($url);
208 $url2 = PageUrl::excludeQueryParametersFromUrl($url, $this->request->getIdSite());
209
210 $this->actionUrl = PageUrl::getUrlIfLookValid($url2);
211
212 if ($url != $this->rawActionUrl) {
213 $this->logger->debug(' Before was "{rawActionUrl}"', [
214 'rawActionUrl' => $this->rawActionUrl,
215 ]);
216 $this->logger->debug(' After is "{url2}"', [
217 'url2' => $url2,
218 ]);
219 }
220 }
221
222 protected function setActionUrlWithoutExcludingParameters($url)
223 {
224 $url = PageUrl::getUrlIfLookValid($url);
225 $this->rawActionUrl = $url;
226 $this->actionUrl = $url;
227 }
228
229 abstract protected function getActionsToLookup();
230
231 protected function getUrlAndType()
232 {
233 $url = $this->getActionUrl();
234
235 if (!empty($url)) {
236 // normalize urls by stripping protocol and www
237 $url = PageUrl::normalizeUrl($url);
238 return array($url['url'], self::TYPE_PAGE_URL, $url['prefixId']);
239 }
240
241 return false;
242 }
243
244 public function setCustomField($field, $value)
245 {
246 $this->customFields[$field] = $value;
247 }
248
249 public function getCustomField($field)
250 {
251 if (isset($this->customFields[$field])) {
252 return $this->customFields[$field];
253 }
254 }
255
256 public function getCustomFields()
257 {
258 return $this->customFields;
259 }
260
261 public function getIdActionUrl()
262 {
263 $idUrl = isset($this->actionIdsCached['idaction_url']) ? $this->actionIdsCached['idaction_url'] : 0;
264 // note; idaction_url = 0 is displayed as "Page URL Not Defined"
265 return (int)$idUrl;
266 }
267
268 public function getIdActionUrlForEntryAndExitIds()
269 {
270 return false;
271 }
272
273 public function getIdActionNameForEntryAndExitIds()
274 {
275 return false;
276 }
277
278 public function getIdActionName()
279 {
280 if (!isset($this->actionIdsCached['idaction_name'])) {
281 return false;
282 }
283
284 return $this->actionIdsCached['idaction_name'];
285 }
286
287 /**
288 * Returns the ID of the newly created record in the log_link_visit_action table
289 *
290 * @return int
291 */
292 public function getIdLinkVisitAction()
293 {
294 return $this->idLinkVisitAction;
295 }
296
297 public static function getTypeAsString($type)
298 {
299 $class = new \ReflectionClass("\\Piwik\\Tracker\\Action");
300 $constants = $class->getConstants();
301
302 $typeId = array_search($type, $constants);
303
304 if (false === $typeId) {
305 return $type;
306 }
307
308 return str_replace('TYPE_', '', $typeId);
309 }
310
311 /**
312 * Loads the idaction of the current action name and the current action url.
313 * These idactions are used in the visitor logging table to link the visit information
314 * (entry action, exit action) to the actions.
315 * These idactions are also used in the table that links the visits and their actions.
316 *
317 * The methods takes care of creating a new record(s) in the action table if the existing
318 * action name and action url doesn't exist yet.
319 */
320 public function loadIdsFromLogActionTable()
321 {
322 if (!empty($this->actionIdsCached)) {
323 return;
324 }
325
326 /** @var ActionDimension[] $dimensions */
327 $dimensions = ActionDimension::getAllDimensions();
328 $actions = $this->getActionsToLookup();
329
330 foreach ($dimensions as $dimension) {
331 $value = $dimension->onLookupAction($this->request, $this);
332
333 if (false !== $value) {
334 if (is_float($value)) {
335 $value = Common::forceDotAsSeparatorForDecimalPoint($value);
336 }
337
338 $field = $dimension->getColumnName();
339
340 if (empty($field)) {
341 $dimensionClass = get_class($dimension);
342 throw new Exception('Dimension ' . $dimensionClass . ' does not define a field name');
343 }
344
345 $actionId = $dimension->getActionId();
346 $actions[$field] = array($value, $actionId);
347 $this->logger->debug("$field = $value");
348 }
349 }
350
351 $actions = array_filter($actions);
352
353 if (empty($actions)) {
354 return;
355 }
356
357 $loadedActionIds = TableLogAction::loadIdsAction($actions);
358
359 $this->actionIdsCached = $loadedActionIds;
360 return $this->actionIdsCached;
361 }
362
363 /**
364 * Records in the DB the association between the visit and this action.
365 *
366 * @param int $idReferrerActionUrl is the ID of the last action done by the current visit.
367 * @param $idReferrerActionName
368 * @param Visitor $visitor
369 */
370 public function record(Visitor $visitor, $idReferrerActionUrl, $idReferrerActionName)
371 {
372 $this->loadIdsFromLogActionTable();
373
374 $visitAction = array(
375 'idvisit' => $visitor->getVisitorColumn('idvisit'),
376 'idsite' => $this->request->getIdSite(),
377 'idvisitor' => $visitor->getVisitorColumn('idvisitor'),
378 'idaction_url' => $this->getIdActionUrl(),
379 'idaction_url_ref' => $idReferrerActionUrl,
380 'idaction_name_ref' => $idReferrerActionName
381 );
382
383 /** @var ActionDimension[] $dimensions */
384 $dimensions = ActionDimension::getAllDimensions();
385
386 foreach ($dimensions as $dimension) {
387 $value = $dimension->onNewAction($this->request, $visitor, $this);
388
389 if ($value !== false) {
390 if (is_float($value)) {
391 $value = Common::forceDotAsSeparatorForDecimalPoint($value);
392 }
393
394 $visitAction[$dimension->getColumnName()] = $value;
395 }
396 }
397
398 // idaction_name is NULLable. we only set it when applicable
399 if ($this->isActionHasActionName()) {
400 $visitAction['idaction_name'] = (int)$this->getIdActionName();
401 }
402
403 foreach ($this->actionIdsCached as $field => $idAction) {
404 $visitAction[$field] = ($idAction === false) ? 0 : $idAction;
405 }
406
407 $customValue = $this->getCustomFloatValue();
408 if ($customValue !== false && $customValue !== null && $customValue !== '') {
409 $visitAction[self::DB_COLUMN_CUSTOM_FLOAT] = Common::forceDotAsSeparatorForDecimalPoint($customValue);
410 }
411
412 $visitAction = array_merge($visitAction, $this->customFields);
413
414 $this->idLinkVisitAction = $this->getModel()->createAction($visitAction);
415
416 $visitAction['idlink_va'] = $this->idLinkVisitAction;
417
418 $visitActionDebug = $visitAction;
419 $visitActionDebug['idvisitor'] = bin2hex($visitActionDebug['idvisitor']);
420 $this->logger->debug("Inserted new action: {action}", [
421 'action' => var_export($visitActionDebug, true),
422 ]);
423 }
424
425 public function writeDebugInfo()
426 {
427 $type = self::getTypeAsString($this->getActionType());
428 $name = $this->getActionName();
429 $url = $this->getActionUrl();
430
431 $this->logger->debug('Action is a {type}, Action name = {name}, Action URL = {url}', [
432 'type' => $type,
433 'name' => $name,
434 'url' => $url,
435 ]);
436
437 return true;
438 }
439
440 private function getModel()
441 {
442 return new Model();
443 }
444
445 /**
446 * @return bool
447 */
448 private function isActionHasActionName()
449 {
450 $types = array(self::TYPE_PAGE_TITLE, self::TYPE_PAGE_URL, self::TYPE_SITE_SEARCH);
451
452 return in_array($this->getActionType(), $types);
453 }
454 }
455