PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.0.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.0.2
5.13.0 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 / TableLogAction.php
matomo / app / core / Tracker Last commit date
Db 5 years ago Handler 5 years ago TableLogAction 5 years ago Visit 5 years ago Action.php 5 years ago ActionPageview.php 5 years ago Cache.php 5 years ago Db.php 5 years ago Failures.php 6 years ago FingerprintSalt.php 6 years ago GoalManager.php 5 years ago Handler.php 5 years ago IgnoreCookie.php 5 years ago LogTable.php 5 years ago Model.php 5 years ago PageUrl.php 5 years ago Request.php 5 years ago RequestProcessor.php 5 years ago RequestSet.php 5 years ago Response.php 5 years ago ScheduledTasksRunner.php 5 years ago Settings.php 5 years ago TableLogAction.php 5 years ago TrackerCodeGenerator.php 5 years ago TrackerConfig.php 5 years ago Visit.php 5 years ago VisitExcluded.php 5 years ago VisitInterface.php 5 years ago Visitor.php 5 years ago VisitorNotFoundInDb.php 5 years ago VisitorRecognizer.php 5 years ago
TableLogAction.php
288 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
10 namespace Piwik\Tracker;
11
12 use Piwik\Common;
13 use Piwik\Container\StaticContainer;
14 use Piwik\Segment\SegmentExpression;
15
16 /**
17 * This class is used to query Action IDs from the log_action table.
18 *
19 * A pageview, outlink, download or site search are made of several "Action IDs"
20 * For example pageview is idaction_url and idaction_name.
21 *
22 */
23 class TableLogAction
24 {
25 /**
26 * This function will find the idaction from the lookup table log_action,
27 * given an Action name, type, and an optional URL Prefix.
28 *
29 * This is used to record Page URLs, Page Titles, Ecommerce items SKUs, item names, item categories
30 *
31 * If the action name does not exist in the lookup table, it will INSERT it
32 * @param array $actionsNameAndType Array of one or many (name,type)
33 * @return array Returns the an array (Field name => idaction)
34 */
35 public static function loadIdsAction($actionsNameAndType)
36 {
37 // Add url prefix if not set
38 foreach ($actionsNameAndType as &$action) {
39 if (2 == count($action)) {
40 $action[] = null;
41 }
42 }
43
44 $actionIds = self::queryIdsAction($actionsNameAndType);
45
46 list($queriedIds, $fieldNamesToInsert) = self::processIdsToInsert($actionsNameAndType, $actionIds);
47
48 $insertedIds = self::insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert);
49 $queriedIds = $queriedIds + $insertedIds;
50
51 return $queriedIds;
52 }
53
54 /**
55 * @param $matchType
56 * @param $actionType
57 * @return string
58 * @throws \Exception
59 */
60 private static function getSelectQueryWhereNameContains($matchType, $actionType)
61 {
62 // now, we handle the cases =@ (contains) and !@ (does not contain)
63 // build the expression based on the match type
64 $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE %s AND type = ' . $actionType . ' )';
65
66 switch ($matchType) {
67 case SegmentExpression::MATCH_CONTAINS:
68 // use concat to make sure, no %s occurs because some plugins use %s in their sql
69 $where = '( name LIKE CONCAT(\'%\', ?, \'%\') ';
70 break;
71 case SegmentExpression::MATCH_DOES_NOT_CONTAIN:
72 $where = '( name NOT LIKE CONCAT(\'%\', ?, \'%\') ';
73 break;
74 case SegmentExpression::MATCH_STARTS_WITH:
75 // use concat to make sure, no %s occurs because some plugins use %s in their sql
76 $where = '( name LIKE CONCAT(?, \'%\') ';
77 break;
78 case SegmentExpression::MATCH_ENDS_WITH:
79 // use concat to make sure, no %s occurs because some plugins use %s in their sql
80 $where = '( name LIKE CONCAT(\'%\', ?) ';
81 break;
82 default:
83 throw new \Exception("This match type $matchType is not available for action-segments.");
84 break;
85 }
86
87 $sql = sprintf($sql, $where);
88
89 return $sql;
90 }
91
92 private static function insertNewIdsAction($actionsNameAndType, $fieldNamesToInsert)
93 {
94 // Then, we insert all new actions in the lookup table
95 $inserted = array();
96
97 foreach ($fieldNamesToInsert as $fieldName) {
98 list($name, $type, $urlPrefix) = $actionsNameAndType[$fieldName];
99
100 $actionId = self::getModel()->createNewIdAction($name, $type, $urlPrefix);
101
102 Common::printDebug("Recorded a new action (" . Action::getTypeAsString($type) . ") in the lookup table: " . $name . " (idaction = " . $actionId . ")");
103
104 $inserted[$fieldName] = $actionId;
105 }
106
107 return $inserted;
108 }
109
110 private static function getModel()
111 {
112 return new Model();
113 }
114
115 private static function queryIdsAction($actionsNameAndType)
116 {
117 $toQuery = array();
118 foreach ($actionsNameAndType as &$actionNameType) {
119 list($name, $type, $urlPrefix) = $actionNameType;
120 $toQuery[] = array('name' => $name, 'type' => $type);
121 }
122
123 $actionIds = self::getModel()->getIdsAction($toQuery);
124
125 return $actionIds;
126 }
127
128 private static function processIdsToInsert($actionsNameAndType, $actionIds)
129 {
130 // For the Actions found in the lookup table, add the idaction in the array,
131 // If not found in lookup table, queue for INSERT
132 $fieldNamesToInsert = $fieldNameToActionId = array();
133
134 foreach ($actionsNameAndType as $fieldName => &$actionNameType) {
135 @list($name, $type, $urlPrefix) = $actionNameType;
136 if (empty($name)) {
137 $fieldNameToActionId[$fieldName] = false;
138 continue;
139 }
140
141 $found = false;
142 foreach ($actionIds as $row) {
143 if ($name == $row['name']
144 && $type == $row['type']
145 ) {
146 $found = true;
147
148 $fieldNameToActionId[$fieldName] = $row['idaction'];
149 continue;
150 }
151 }
152 if (!$found) {
153 $fieldNamesToInsert[] = $fieldName;
154 }
155 }
156
157 return array($fieldNameToActionId, $fieldNamesToInsert);
158 }
159
160 /**
161 * Convert segment expression to an action ID or an SQL expression.
162 *
163 * This method is used as a sqlFilter-callback for the segments of this plugin.
164 * Usually, these callbacks only return a value that should be compared to the
165 * column in the database. In this case, that doesn't work since multiple IDs
166 * can match an expression (e.g. "pageUrl=@foo").
167 * @param string $valueToMatch
168 * @param string $sqlField
169 * @param string $matchType
170 * @param string $segmentName
171 * @throws \Exception
172 * @return array|int|string
173 */
174 public static function getIdActionFromSegment($valueToMatch, $sqlField, $matchType, $segmentName)
175 {
176 if ($segmentName === 'actionType') {
177 $actionType = (int) $valueToMatch;
178 $valueToMatch = array();
179 $sql = 'SELECT idaction FROM ' . Common::prefixTable('log_action') . ' WHERE type = ' . $actionType . ' )';
180 } else {
181 $actionType = self::guessActionTypeFromSegment($segmentName);
182 if ($actionType == Action::TYPE_PAGE_URL || $segmentName == 'eventUrl') {
183 // for urls trim protocol and www because it is not recorded in the db
184 $valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch);
185 }
186
187 $valueToMatch = self::normaliseActionString($actionType, $valueToMatch);
188 if ($matchType == SegmentExpression::MATCH_EQUAL
189 || $matchType == SegmentExpression::MATCH_NOT_EQUAL
190 ) {
191 $idAction = self::getModel()->getIdActionMatchingNameAndType($valueToMatch, $actionType);
192 // Action is not found (eg. &segment=pageTitle==Větrnásssssss)
193 if (empty($idAction)) {
194 $idAction = null;
195 }
196 return $idAction;
197 }
198
199 // "name contains $string" match can match several idaction so we cannot return yet an idaction
200 // special case
201 $sql = self::getSelectQueryWhereNameContains($matchType, $actionType);
202 }
203
204
205 $cache = StaticContainer::get('Piwik\Tracker\TableLogAction\Cache');
206 return $cache->getIdActionFromSegment($valueToMatch, $sql);
207 }
208
209 /**
210 * @param $segmentName
211 * @return int
212 * @throws \Exception
213 */
214 private static function guessActionTypeFromSegment($segmentName)
215 {
216 $exactMatch = array(
217 'outlinkUrl' => Action::TYPE_OUTLINK,
218 'downloadUrl' => Action::TYPE_DOWNLOAD,
219 'eventUrl' => Action::TYPE_EVENT,
220 'eventAction' => Action::TYPE_EVENT_ACTION,
221 'eventCategory' => Action::TYPE_EVENT_CATEGORY,
222 'eventName' => Action::TYPE_EVENT_NAME,
223 'contentPiece' => Action::TYPE_CONTENT_PIECE,
224 'contentTarget' => Action::TYPE_CONTENT_TARGET,
225 'contentName' => Action::TYPE_CONTENT_NAME,
226 'contentInteraction' => Action::TYPE_CONTENT_INTERACTION,
227 'productName' => Action::TYPE_ECOMMERCE_ITEM_NAME,
228 'productSku' => Action::TYPE_ECOMMERCE_ITEM_SKU,
229 'productViewName' => Action::TYPE_ECOMMERCE_ITEM_NAME,
230 'productViewSku' => Action::TYPE_ECOMMERCE_ITEM_SKU
231 );
232
233 if (!empty($exactMatch[$segmentName])) {
234 return $exactMatch[$segmentName];
235 }
236
237 if (stripos($segmentName, 'pageurl') !== false) {
238 return Action::TYPE_PAGE_URL;
239 } elseif (stripos($segmentName, 'pagetitle') !== false) {
240 return Action::TYPE_PAGE_TITLE;
241 } elseif (stripos($segmentName, 'sitesearch') !== false) {
242 return Action::TYPE_SITE_SEARCH;
243 } elseif (stripos($segmentName, 'productcategory') !== false
244 || stripos($segmentName, 'productviewcategory') !== false) {
245 return Action::TYPE_ECOMMERCE_ITEM_CATEGORY;
246 } else {
247 throw new \Exception("We cannot guess the action type from the segment $segmentName.");
248 }
249 }
250
251 /**
252 * This function will sanitize or not if it's needed for the specified action type
253 *
254 * URLs (Download URL, Outlink URL) are stored raw (unsanitized)
255 * while other action types are stored Sanitized
256 *
257 * @param $actionType
258 * @param $actionString
259 * @return string
260 */
261 private static function normaliseActionString($actionType, $actionString)
262 {
263 $actionString = Common::unsanitizeInputValue($actionString);
264
265 if (self::isActionTypeStoredUnsanitized($actionType)) {
266 return $actionString;
267 }
268
269 return Common::sanitizeInputValue($actionString);
270 }
271
272 /**
273 * @param $actionType
274 * @return bool
275 */
276 private static function isActionTypeStoredUnsanitized($actionType)
277 {
278 $actionsTypesStoredUnsanitized = array(
279 Action::TYPE_DOWNLOAD,
280 Action::TYPE_OUTLINK,
281 Action::TYPE_PAGE_URL,
282 Action::TYPE_CONTENT,
283 );
284
285 return in_array($actionType, $actionsTypesStoredUnsanitized);
286 }
287 }
288