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