| /** | |
| * Matomo - free/libre analytics platform | |
| * | |
| * @link https://matomo.org | |
| * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later | |
| */ | |
| namespace Piwik\Columns; | |
| use Piwik\Common; | |
| use Piwik\Db; | |
| use Piwik\Piwik; | |
| use Piwik\Plugin; | |
| use Piwik\Plugin\ArchivedMetric; | |
| use Piwik\Plugin\Segment; | |
| use Exception; | |
| use Piwik\CacheId; | |
| use Piwik\Cache as PiwikCache; | |
| use Piwik\Plugin\Manager as PluginManager; | |
| use Piwik\Metrics\Formatter; | |
| use Piwik\Segment\SegmentsList; | |
| /** | |
| * @api | |
| * @since 3.1.0 | |
| */ | |
| abstract class Dimension | |
| { | |
| public const COMPONENT_SUBNAMESPACE = 'Columns'; | |
| /** | |
| * Segment type 'dimension'. Can be used along with {@link setType()}. | |
| * @api | |
| */ | |
| public const TYPE_DIMENSION = 'dimension'; | |
| public const TYPE_BINARY = 'binary'; | |
| public const TYPE_TEXT = 'text'; | |
| public const TYPE_ENUM = 'enum'; | |
| public const TYPE_MONEY = 'money'; | |
| public const TYPE_BYTE = 'byte'; | |
| public const TYPE_DURATION_MS = 'duration_ms'; | |
| public const TYPE_DURATION_S = 'duration_s'; | |
| public const TYPE_NUMBER = 'number'; | |
| public const TYPE_FLOAT = 'float'; | |
| public const TYPE_URL = 'url'; | |
| public const TYPE_DATE = 'date'; | |
| public const TYPE_TIME = 'time'; | |
| public const TYPE_DATETIME = 'datetime'; | |
| public const TYPE_TIMESTAMP = 'timestamp'; | |
| public const TYPE_BOOL = 'bool'; | |
| public const TYPE_PERCENT = 'percent'; | |
| /** | |
| * This will be the name of the column in the database table if a $columnType is specified. | |
| * @var string | |
| * @api | |
| */ | |
| protected $columnName = ''; | |
| /** | |
| * If a columnType is defined, we will create a column in the MySQL table having this type. Please make sure | |
| * MySQL understands this type. Once you change the column type the Piwik platform will notify the user to | |
| * perform an update which can sometimes take a long time so be careful when choosing the correct column type. | |
| * @var string | |
| * @api | |
| */ | |
| protected $columnType = ''; | |
| /** | |
| * Holds an array of segment instances | |
| * @var Segment[] | |
| */ | |
| protected $segments = array(); | |
| /** | |
| * Defines what kind of data type this dimension holds. By default the type is auto-detected based on | |
| * `$columnType` but sometimes it may be needed to correct this value. Depending on this type, a dimension will be | |
| * formatted differently for example. | |
| * @var string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $type = ''; | |
| /** | |
| * Translation key for name singular | |
| * @var string | |
| */ | |
| protected $nameSingular = ''; | |
| /** | |
| * Translation key for name plural | |
| * @var string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $namePlural = ''; | |
| /** | |
| * Translation key for category | |
| * @var string | |
| */ | |
| protected $category = ''; | |
| /** | |
| * By defining a segment name a user will be able to filter their visitors by this column. If you do not want to | |
| * define a segment for this dimension, simply leave the name empty. | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $segmentName = ''; | |
| /** | |
| * Sets a callback which will be executed when user will call for suggested values for segment. | |
| * | |
| * @var callable | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $suggestedValuesCallback; | |
| /** | |
| * An API method whose label columns will be used to determine the suggested values should browser archiving | |
| * be disabled. The API must have defined a segment metadata on each row for this to work. | |
| * @var string | |
| */ | |
| protected $suggestedValuesApi = ''; | |
| /** | |
| * Here you should explain which values are accepted/useful for your segment, for example: | |
| * "1, 2, 3, etc." or "comcast.net, proxad.net, etc.". If the value needs any special encoding you should mention | |
| * this as well. For example "Any URL including protocol. The URL must be URL encoded." | |
| * | |
| * @var string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $acceptValues; | |
| /** | |
| * Defines to which column in the MySQL database the segment belongs (if one is configured). Defaults to | |
| * `$this.dbTableName . '.'. $this.columnName` but you can customize it eg like `HOUR(log_visit.visit_last_action_time)`. | |
| * | |
| * @param string $sqlSegment | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $sqlSegment; | |
| /** | |
| * Interesting when specifying a segment. Sometimes you want users to set segment values that differ from the way | |
| * they are actually stored. For instance if you want to allow to filter by any URL than you might have to resolve | |
| * this URL to an action id. Or a country name maybe has to be mapped to a 2 letter country code. You can do this by | |
| * specifying either a callable such as `array('Classname', 'methodName')` or by passing a closure. | |
| * There will be four values passed to the given closure or callable: `string $valueToMatch`, `string $segment` | |
| * (see {@link setSegment()}), `string $matchType` (eg SegmentExpression::MATCH_EQUAL or any other match constant | |
| * of this class) and `$segmentName`. | |
| * | |
| * If the closure returns NULL, then Piwik assumes the segment sub-string will not match any visitor. | |
| * | |
| * @var string|\Closure|callable | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $sqlFilter; | |
| /** | |
| * Similar to {@link $sqlFilter} you can map a given segment value to another value. For instance you could map | |
| * "new" to 0, 'returning' to 1 and any other value to '2'. You can either define a callable or a closure. There | |
| * will be only one value passed to the closure or callable which contains the value a user has set for this | |
| * segment. | |
| * @var string|array | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $sqlFilterValue; | |
| /** | |
| * Defines whether this dimension (and segment based on this dimension) is available to anonymous users. | |
| * @var bool | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $allowAnonymous = true; | |
| /** | |
| * The name of the database table this dimension refers to | |
| * @var string | |
| * @api | |
| */ | |
| protected $dbTableName = ''; | |
| /** | |
| * By default the metricId is automatically generated based on the dimensionId. This might sometimes not be as | |
| * readable and quite long. If you want more expressive metric names like `nb_visits` compared to | |
| * `nb_corehomevisitid`, you can eg set a metricId `visit`. | |
| * | |
| * @var string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| protected $metricId = ''; | |
| /** | |
| * To be implemented when a column references another column | |
| * @return Join|null | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getDbColumnJoin() | |
| { | |
| return null; | |
| } | |
| /** | |
| * @return Discriminator|null | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getDbDiscriminator() | |
| { | |
| return null; | |
| } | |
| /** | |
| * To be implemented when a column represents an enum. | |
| * @return array | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getEnumColumnValues() | |
| { | |
| return array(); | |
| } | |
| /** | |
| * Get the metricId which is used to generate metric names based on this dimension. | |
| * @return string | |
| */ | |
| public function getMetricId() | |
| { | |
| if (!empty($this->metricId)) { | |
| return $this->metricId; | |
| } | |
| $id = $this->getId(); | |
| return str_replace(array('.', ' ', '-'), '_', strtolower($id)); | |
| } | |
| /** | |
| * Installs the action dimension in case it is not installed yet. The installation is already implemented based on | |
| * the {@link $columnName} and {@link $columnType}. If you want to perform additional actions beside adding the | |
| * column to the database - for instance adding an index - you can overwrite this method. We recommend to call | |
| * this parent method to get the minimum required actions and then add further custom actions since this makes sure | |
| * the column will be installed correctly. We also recommend to change the default install behavior only if really | |
| * needed. FYI: We do not directly execute those alter table statements here as we group them together with several | |
| * other alter table statements do execute those changes in one step which results in a faster installation. The | |
| * column will be added to the `log_link_visit_action` MySQL table. | |
| * | |
| * Example: | |
| * ``` | |
| public function install() | |
| { | |
| $changes = parent::install(); | |
| $changes['log_link_visit_action'][] = "ADD INDEX index_idsite_servertime ( idsite, server_time )"; | |
| return $changes; | |
| } | |
| ``` | |
| * | |
| * @return array An array containing the table name as key and an array of MySQL alter table statements that should | |
| * be executed on the given table. Example: | |
| * ``` | |
| array( | |
| 'log_link_visit_action' => array("ADD COLUMN `$this->columnName` $this->columnType", "ADD INDEX ...") | |
| ); | |
| ``` | |
| * @api | |
| */ | |
| public function install() | |
| { | |
| if (empty($this->columnName) || empty($this->columnType) || empty($this->dbTableName)) { | |
| return array(); | |
| } | |
| // TODO if table does not exist, create it with a primary key, but at this point we cannot really create it | |
| // cause we need to show the query in the UI first and user needs to be able to create table manually. | |
| // we cannot return something like "create table " here as it would be returned for each table etc. | |
| // we need to do this in column updater etc! | |
| return array( | |
| $this->dbTableName => array("ADD COLUMN `$this->columnName` $this->columnType"), | |
| ); | |
| } | |
| /** | |
| * Updates the action dimension in case the {@link $columnType} has changed. The update is already implemented based | |
| * on the {@link $columnName} and {@link $columnType}. This method is intended not to overwritten by plugin | |
| * developers as it is only supposed to make sure the column has the correct type. Adding additional custom "alter | |
| * table" actions would not really work since they would be executed with every {@link $columnType} change. So | |
| * adding an index here would be executed whenever the columnType changes resulting in an error if the index already | |
| * exists. If an index needs to be added after the first version is released a plugin update class should be | |
| * created since this makes sure it is only executed once. | |
| * | |
| * @return array An array containing the table name as key and an array of MySQL alter table statements that should | |
| * be executed on the given table. Example: | |
| * ``` | |
| array( | |
| 'log_link_visit_action' => array("MODIFY COLUMN `$this->columnName` $this->columnType", "DROP COLUMN ...") | |
| ); | |
| ``` | |
| * @ignore | |
| */ | |
| public function update() | |
| { | |
| if (empty($this->columnName) || empty($this->columnType) || empty($this->dbTableName)) { | |
| return array(); | |
| } | |
| return array( | |
| $this->dbTableName => array("MODIFY COLUMN `$this->columnName` $this->columnType"), | |
| ); | |
| } | |
| /** | |
| * Uninstalls the dimension if a {@link $columnName} and {@link columnType} is set. In case you perform any custom | |
| * actions during {@link install()} - for instance adding an index - you should make sure to undo those actions by | |
| * overwriting this method. Make sure to call this parent method to make sure the uninstallation of the column | |
| * will be done. | |
| * @throws Exception | |
| * @api | |
| */ | |
| public function uninstall() | |
| { | |
| if (empty($this->columnName) || empty($this->columnType) || empty($this->dbTableName)) { | |
| return; | |
| } | |
| try { | |
| $sql = "ALTER TABLE `" . Common::prefixTable($this->dbTableName) . "` DROP COLUMN `$this->columnName`"; | |
| Db::exec($sql); | |
| } catch (Exception $e) { | |
| if (!Db::get()->isErrNo($e, '1091')) { | |
| throw $e; | |
| } | |
| } | |
| } | |
| /** | |
| * Returns the ID of the category (typically a translation key). | |
| * @return string | |
| */ | |
| public function getCategoryId() | |
| { | |
| return $this->category; | |
| } | |
| /** | |
| * Returns the translated name of this dimension which is typically in singular. | |
| * | |
| * @return string | |
| */ | |
| public function getName() | |
| { | |
| if (!empty($this->nameSingular)) { | |
| return Piwik::translate($this->nameSingular); | |
| } | |
| return $this->nameSingular; | |
| } | |
| /** | |
| * Returns a translated name in plural for this dimension. | |
| * @return string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getNamePlural() | |
| { | |
| if (!empty($this->namePlural)) { | |
| return Piwik::translate($this->namePlural); | |
| } | |
| return $this->getName(); | |
| } | |
| /** | |
| * Defines whether an anonymous user is allowed to view this dimension | |
| * @return bool | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function isAnonymousAllowed() | |
| { | |
| return $this->allowAnonymous; | |
| } | |
| /** | |
| * Sets (overwrites) the SQL segment | |
| * @param $segment | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function setSqlSegment($segment) | |
| { | |
| $this->sqlSegment = $segment; | |
| } | |
| /** | |
| * Sets (overwrites the dimension type) | |
| * @param $type | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function setType($type) | |
| { | |
| $this->type = $type; | |
| } | |
| /** | |
| * A dimension should group values by using this method. Otherwise the same row may appear several times. | |
| * | |
| * @param mixed $value | |
| * @param int $idSite | |
| * @return mixed | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function groupValue($value, $idSite) | |
| { | |
| switch ($this->type) { | |
| case Dimension::TYPE_URL: | |
| return str_replace(array('http://', 'https://'), '', $value); | |
| case Dimension::TYPE_BOOL: | |
| return !empty($value) ? '1' : '0'; | |
| case Dimension::TYPE_DURATION_MS: | |
| if (!is_numeric($value)) { | |
| // This might happen if ranking query has too many results and `__mtm_ranking_query_others__` is returned | |
| return $value; | |
| } | |
| return round($value / 1000, 2) * 1000; // because we divide we need to group them and cannot do this in formatting step | |
| } | |
| return $value; | |
| } | |
| /** | |
| * Formats the dimension value. By default, the dimension is formatted based on the set dimension type. | |
| * | |
| * @param mixed $value | |
| * @param int $idSite | |
| * @return mixed | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function formatValue($value, $idSite, Formatter $formatter) | |
| { | |
| switch ($this->type) { | |
| case Dimension::TYPE_BOOL: | |
| if (empty($value)) { | |
| return Piwik::translate('General_No'); | |
| } | |
| return Piwik::translate('General_Yes'); | |
| case Dimension::TYPE_ENUM: | |
| $values = $this->getEnumColumnValues(); | |
| if (isset($values[$value])) { | |
| return $values[$value]; | |
| } | |
| break; | |
| case Dimension::TYPE_MONEY: | |
| return $formatter->getPrettyMoney($value, $idSite); | |
| case Dimension::TYPE_FLOAT: | |
| return $formatter->getPrettyNumber((float) $value, $precision = 2); | |
| case Dimension::TYPE_NUMBER: | |
| return $formatter->getPrettyNumber($value); | |
| case Dimension::TYPE_DURATION_S: | |
| return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = false); | |
| case Dimension::TYPE_DURATION_MS: | |
| $val = round(($value / 1000), ($value / 1000) > 60 ? 0 : 2); | |
| return $formatter->getPrettyTimeFromSeconds($val, $displayAsSentence = true); | |
| case Dimension::TYPE_PERCENT: | |
| return $formatter->getPrettyPercentFromQuotient($value); | |
| case Dimension::TYPE_BYTE: | |
| return $formatter->getPrettySizeFromBytes($value); | |
| } | |
| return $value; | |
| } | |
| /** | |
| * Overwrite this method to configure segments. To do so just create an instance of a {@link \Piwik\Plugin\Segment} | |
| * class, configure it and call the {@link addSegment()} method. You can add one or more segments for this | |
| * dimension. Example: | |
| * | |
| * ``` | |
| * $segment = new Segment(); | |
| * $segment->setSegment('exitPageUrl'); | |
| * $segment->setName('Actions_ColumnExitPageURL'); | |
| * $segment->setCategory('General_Visit'); | |
| * $segmentsList->addSegment($segment); | |
| * ``` | |
| * | |
| * @throws Exception | |
| */ | |
| public function configureSegments(SegmentsList $segmentsList, DimensionSegmentFactory $dimensionSegmentFactory) | |
| { | |
| if ( | |
| $this->segmentName && $this->category | |
| && ($this->sqlSegment || ($this->columnName && $this->dbTableName)) | |
| && $this->nameSingular | |
| ) { | |
| $segment = $dimensionSegmentFactory->createSegment(null); | |
| $segmentsList->addSegment($segment); | |
| } | |
| } | |
| /** | |
| * Configures metrics for this dimension. | |
| * | |
| * For certain dimension types, some metrics will be added automatically. | |
| */ | |
| public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory) | |
| { | |
| if ($this->getMetricId() && $this->dbTableName && $this->columnName && $this->getNamePlural()) { | |
| if (in_array($this->getType(), array(self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME, self::TYPE_TIMESTAMP))) { | |
| // we do not generate any metrics from these types | |
| return; | |
| } elseif (in_array($this->getType(), array(self::TYPE_URL, self::TYPE_TEXT, self::TYPE_BINARY, self::TYPE_ENUM))) { | |
| $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE); | |
| $metricsList->addMetric($metric); | |
| } elseif (in_array($this->getType(), array(self::TYPE_BOOL))) { | |
| $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM); | |
| $metricsList->addMetric($metric); | |
| } else { | |
| $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM); | |
| $metricsList->addMetric($metric); | |
| $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_MAX); | |
| $metricsList->addMetric($metric); | |
| } | |
| } | |
| } | |
| /** | |
| * Check whether a dimension has overwritten a specific method. | |
| * @param $method | |
| * @return bool | |
| * @ignore | |
| */ | |
| public function hasImplementedEvent($method) | |
| { | |
| $method = new \ReflectionMethod($this, $method); | |
| $declaringClass = $method->getDeclaringClass(); | |
| return 0 === strpos($declaringClass->name, 'Piwik\Plugins'); | |
| } | |
| /** | |
| * Get the list of configured segments. | |
| * | |
| * @return Segment[] | |
| * @throws Exception | |
| * @ignore | |
| */ | |
| public function getSegments() | |
| { | |
| $list = new SegmentsList(); | |
| $this->configureSegments($list, new DimensionSegmentFactory($this)); | |
| return $list->getSegments(); | |
| } | |
| /** | |
| * Returns the name of the segment that this dimension defines | |
| * @return string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getSegmentName() | |
| { | |
| return $this->segmentName; | |
| } | |
| /** | |
| * Get the name of the dimension column. | |
| * @return string | |
| * @ignore | |
| */ | |
| public function getColumnName() | |
| { | |
| return $this->columnName; | |
| } | |
| /** | |
| * Returns a sql segment expression for this dimension. | |
| * @return string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getSqlSegment() | |
| { | |
| if (!empty($this->sqlSegment)) { | |
| return $this->sqlSegment; | |
| } | |
| if ($this->dbTableName && $this->columnName) { | |
| return $this->dbTableName . '.' . $this->columnName; | |
| } | |
| return ''; | |
| } | |
| /** | |
| * @return null|callable | |
| * @ignore | |
| */ | |
| public function getSuggestedValuesCallback() | |
| { | |
| return $this->suggestedValuesCallback; | |
| } | |
| /** | |
| * @return null|string | |
| * @ignore | |
| */ | |
| public function getSuggestedValuesApi() | |
| { | |
| return $this->suggestedValuesApi; | |
| } | |
| /** | |
| * @return null|string | |
| * @ignore | |
| */ | |
| public function getAcceptValues() | |
| { | |
| if (!empty($this->acceptValues) && strpos($this->acceptValues, '_')) { | |
| return Piwik::translate($this->acceptValues); | |
| } | |
| return $this->acceptValues; | |
| } | |
| /** | |
| * @return callable|\Closure|string|null | |
| * @ignore | |
| */ | |
| public function getSqlFilter() | |
| { | |
| return $this->sqlFilter; | |
| } | |
| /** | |
| * @return array|string|null | |
| * @ignore | |
| */ | |
| public function getSqlFilterValue() | |
| { | |
| return $this->sqlFilterValue; | |
| } | |
| /** | |
| * Check whether the dimension has a column type configured | |
| * @return bool | |
| * @ignore | |
| */ | |
| public function hasColumnType() | |
| { | |
| return !empty($this->columnType); | |
| } | |
| /** | |
| * Returns the name of the database table this dimension belongs to. | |
| * @return string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getDbTableName() | |
| { | |
| return $this->dbTableName; | |
| } | |
| /** | |
| * Returns a unique string ID for this dimension. The ID is built using the namespaced class name | |
| * of the dimension, but is modified to be more human readable. | |
| * | |
| * @return string eg, `"Referrers.Keywords"` | |
| * @throws Exception if the plugin and simple class name of this instance cannot be determined. | |
| * This would only happen if the dimension is located in the wrong directory. | |
| * @api | |
| */ | |
| public function getId() | |
| { | |
| $className = get_class($this); | |
| return $this->generateIdFromClass($className); | |
| } | |
| /** | |
| * @param string $className | |
| * @return string | |
| * @throws Exception | |
| * @ignore | |
| */ | |
| protected function generateIdFromClass($className) | |
| { | |
| // parse plugin name & dimension name | |
| $regex = "/Piwik\\\\Plugins\\\\([^\\\\]+)\\\\" . self::COMPONENT_SUBNAMESPACE . "\\\\([^\\\\]+)/"; | |
| if (!preg_match($regex, $className, $matches)) { | |
| throw new Exception("'$className' is located in the wrong directory."); | |
| } | |
| $pluginName = $matches[1]; | |
| $dimensionName = $matches[2]; | |
| return $pluginName . '.' . $dimensionName; | |
| } | |
| /** | |
| * Gets an instance of all available visit, action and conversion dimension. | |
| * @return Dimension[] | |
| */ | |
| public static function getAllDimensions() | |
| { | |
| $cacheId = CacheId::siteAware(CacheId::pluginAware('AllDimensions')); | |
| $cache = PiwikCache::getTransientCache(); | |
| if (!$cache->contains($cacheId)) { | |
| $plugins = PluginManager::getInstance()->getPluginsLoadedAndActivated(); | |
| $instances = array(); | |
| /** | |
| * Triggered to add new dimensions that cannot be picked up automatically by the platform. | |
| * This is useful if the plugin allows a user to create reports / dimensions dynamically. For example | |
| * CustomDimensions or CustomVariables. There are a variable number of dimensions in this case and it | |
| * wouldn't be really possible to create a report file for one of these dimensions as it is not known | |
| * how many Custom Dimensions will exist. | |
| * | |
| * **Example** | |
| * | |
| * public function addDimension(&$dimensions) | |
| * { | |
| * $dimensions[] = new MyCustomDimension(); | |
| * } | |
| * | |
| * @param Dimension[] $reports An array of dimensions | |
| */ | |
| Piwik::postEvent('Dimension.addDimensions', array(&$instances)); | |
| foreach ($plugins as $plugin) { | |
| foreach (self::getDimensions($plugin) as $instance) { | |
| $instances[] = $instance; | |
| } | |
| } | |
| /** | |
| * Triggered to filter / restrict dimensions. | |
| * | |
| * **Example** | |
| * | |
| * public function filterDimensions(&$dimensions) | |
| * { | |
| * foreach ($dimensions as $index => $dimension) { | |
| * if ($dimension->getName() === 'Page URL') {} | |
| * unset($dimensions[$index]); // remove this dimension | |
| * } | |
| * } | |
| * } | |
| * | |
| * @param Dimension[] $dimensions An array of dimensions | |
| */ | |
| Piwik::postEvent('Dimension.filterDimensions', array(&$instances)); | |
| $cache->save($cacheId, $instances); | |
| } | |
| return $cache->fetch($cacheId); | |
| } | |
| public static function getDimensions(Plugin $plugin) | |
| { | |
| $columns = $plugin->findMultipleComponents('Columns', '\\Piwik\\Columns\\Dimension'); | |
| $instances = array(); | |
| $removedDimensions = self::getRemovedDimensions(); | |
| foreach ($columns as $column) { | |
| if (!in_array($column, $removedDimensions)) { | |
| $instances[] = new $column(); | |
| } | |
| } | |
| return $instances; | |
| } | |
| /** | |
| * Returns a list of dimension class names that have been removed from core over time | |
| * | |
| * @return string[] | |
| */ | |
| public static function getRemovedDimensions() | |
| { | |
| return [ | |
| // dimensions removed in Matomo 4.0.0 | |
| 'Piwik\Plugins\DevicePlugins\Columns\PluginDirector', | |
| 'Piwik\Plugins\DevicePlugins\Columns\PluginGears', | |
| 'Piwik\Plugins\VisitorInterest\Columns\VisitsByDaysSinceLastVisit', | |
| ]; | |
| } | |
| /** | |
| * Returns the name of the plugin that contains this Dimension. | |
| * | |
| * @return string | |
| * @throws Exception if the Dimension is not located within a Plugin module. | |
| * @api | |
| */ | |
| public function getModule() | |
| { | |
| $id = $this->getId(); | |
| if (empty($id)) { | |
| throw new Exception("Invalid dimension ID: '$id'."); | |
| } | |
| $parts = explode('.', $id); | |
| return reset($parts); | |
| } | |
| /** | |
| * Returns the type of the dimension which defines what kind of value this dimension stores. | |
| * @return string | |
| * @api since Piwik 3.2.0 | |
| */ | |
| public function getType() | |
| { | |
| if (!empty($this->type)) { | |
| return $this->type; | |
| } | |
| if ($this->getDbColumnJoin()) { | |
| // best guess | |
| return self::TYPE_TEXT; | |
| } | |
| if ($this->getEnumColumnValues()) { | |
| // best guess | |
| return self::TYPE_ENUM; | |
| } | |
| if (!empty($this->columnType)) { | |
| // best guess | |
| $type = strtolower($this->columnType); | |
| if (strpos($type, 'datetime') !== false) { | |
| return self::TYPE_DATETIME; | |
| } elseif (strpos($type, 'timestamp') !== false) { | |
| return self::TYPE_TIMESTAMP; | |
| } elseif (strpos($type, 'date') !== false) { | |
| return self::TYPE_DATE; | |
| } elseif (strpos($type, 'time') !== false) { | |
| return self::TYPE_TIME; | |
| } elseif (strpos($type, 'float') !== false) { | |
| return self::TYPE_FLOAT; | |
| } elseif (strpos($type, 'decimal') !== false) { | |
| return self::TYPE_FLOAT; | |
| } elseif (strpos($type, 'int') !== false) { | |
| return self::TYPE_NUMBER; | |
| } elseif (strpos($type, 'binary') !== false) { | |
| return self::TYPE_BINARY; | |
| } | |
| } | |
| return self::TYPE_TEXT; | |
| } | |
| /** | |
| * Get the version of the dimension which is used for update checks. | |
| * @return string | |
| * @ignore | |
| */ | |
| public function getVersion() | |
| { | |
| return $this->columnType; | |
| } | |
| } | |