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