diff options
Diffstat (limited to 'plugins')
322 files changed, 8192 insertions, 3952 deletions
diff --git a/plugins/API/API.php b/plugins/API/API.php index 4ea0cb4055..8245d0712e 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -10,7 +10,10 @@ namespace Piwik\Plugins\API; use Piwik\API\Proxy; use Piwik\API\Request; -use Piwik\Columns\Dimension; +use Piwik\Cache; +use Piwik\CacheId; +use Piwik\Category\CategoryList; +use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\DataTable; @@ -23,11 +26,13 @@ use Piwik\Period; use Piwik\Period\Range; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; +use Piwik\Plugin\Report; use Piwik\Plugins\API\DataTable\MergeDataTables; use Piwik\Plugins\CoreAdminHome\CustomLogo; use Piwik\Translation\Translator; use Piwik\Measurable\Type\TypeManager; use Piwik\Version; +use Piwik\Widget\WidgetsList; require_once PIWIK_INCLUDE_PATH . '/core/Config.php'; @@ -122,83 +127,20 @@ class API extends \Piwik\Plugin\API { $isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous(); - $segments = array(); - foreach (Dimension::getAllDimensions() as $dimension) { - foreach ($dimension->getSegments() as $segment) { - if ($segment->isRequiresAtLeastViewAccess()) { - $segment->setPermission($isAuthenticatedWithViewAccess); - } + $sites = (is_array($idSites) ? implode('.', $idSites) : (int) $idSites); + $cache = Cache::getTransientCache(); + $cachKey = 'API.getSegmentsMetadata' . $sites . '_' . (int) $_hideImplementationData . '_' . (int) $isAuthenticatedWithViewAccess; + $cachKey = CacheId::pluginAware($cachKey); - $segments[] = $segment->toArray(); - } + if ($cache->contains($cachKey)) { + return $cache->fetch($cachKey); } - /** - * Triggered when gathering all available segment dimensions. - * - * This event can be used to make new segment dimensions available. - * - * **Example** - * - * public function getSegmentsMetadata(&$segments, $idSites) - * { - * $segments[] = array( - * 'type' => 'dimension', - * 'category' => Piwik::translate('General_Visit'), - * 'name' => 'General_VisitorIP', - * 'segment' => 'visitIp', - * 'acceptedValues' => '13.54.122.1, etc.', - * 'sqlSegment' => 'log_visit.location_ip', - * 'sqlFilter' => array('Piwik\IP', 'P2N'), - * 'permission' => $isAuthenticatedWithViewAccess, - * ); - * } - * - * @param array &$dimensions The list of available segment dimensions. Append to this list to add - * new segments. Each element in this list must contain the - * following information: - * - * - **type**: Either `'metric'` or `'dimension'`. `'metric'` means - * the value is a numeric and `'dimension'` means it is - * a string. Also, `'metric'` values will be displayed - * under **Visit (metrics)** in the Segment Editor. - * - **category**: The segment category name. This can be an existing - * segment category visible in the segment editor. - * - **name**: The pretty name of the segment. Can be a translation token. - * - **segment**: The segment name, eg, `'visitIp'` or `'searches'`. - * - **acceptedValues**: A string describing one or two exacmple values, eg - * `'13.54.122.1, etc.'`. - * - **sqlSegment**: The table column this segment will segment by. - * For example, `'log_visit.location_ip'` for the - * **visitIp** segment. - * - **sqlFilter**: A PHP callback to apply to segment values before - * they are used in SQL. - * - **permission**: True if the current user has view access to this - * segment, false if otherwise. - * @param array $idSites The list of site IDs we're getting the available segments - * for. Some segments (such as Goal segments) depend on the - * site. - */ - Piwik::postEvent('API.getSegmentDimensionMetadata', array(&$segments, $idSites)); - - foreach ($segments as &$segment) { - $segment['name'] = Piwik::translate($segment['name']); - $segment['category'] = Piwik::translate($segment['category']); - - if ($_hideImplementationData) { - unset($segment['sqlFilter']); - unset($segment['sqlFilterValue']); - unset($segment['sqlSegment']); - - if (isset($segment['suggestedValuesCallback']) - && !is_string($segment['suggestedValuesCallback']) - ) { - unset($segment['suggestedValuesCallback']); - } - } - } + $metadata = new SegmentMetadata(); + $segments = $metadata->getSegmentsMetadata($idSites, $_hideImplementationData, $isAuthenticatedWithViewAccess); + + $cache->save($cachKey, $segments); - usort($segments, array($this, 'sortSegments')); return $segments; } @@ -219,32 +161,6 @@ class API extends \Piwik\Plugin\API return $values; } - private function sortSegments($row1, $row2) - { - $customVarCategory = Piwik::translate('CustomVariables_CustomVariables'); - - $columns = array('type', 'category', 'name', 'segment'); - foreach ($columns as $column) { - // Keep segments ordered alphabetically inside categories.. - $type = -1; - if ($column == 'name') $type = 1; - - $compare = $type * strcmp($row1[$column], $row2[$column]); - - // hack so that custom variables "page" are grouped together in the doc - if ($row1['category'] == $customVarCategory - && $row1['category'] == $row2['category'] - ) { - $compare = strcmp($row1['segment'], $row2['segment']); - return $compare; - } - if ($compare != 0) { - return $compare; - } - } - return $compare; - } - /** * Returns the url to application logo (~280x110px) * @@ -343,6 +259,41 @@ class API extends \Piwik\Plugin\API } /** + * Get a list of all pages that shall be shown in a Piwik UI including a list of all widgets that shall + * be shown within each page. + * + * @param int $idSite + * @return array + */ + public function getReportPagesMetadata($idSite) + { + Piwik::checkUserHasViewAccess($idSite); + + $widgetsList = WidgetsList::get(); + $categoryList = CategoryList::get(); + $metadata = new WidgetMetadata(); + + return $metadata->getPagesMetadata($categoryList, $widgetsList); + } + + /** + * Get a list of all widgetizable widgets. + * + * @param int $idSite + * @return array + */ + public function getWidgetMetadata($idSite) + { + Piwik::checkUserHasViewAccess($idSite); + + $widgetsList = WidgetsList::get(); + $categoryList = CategoryList::get(); + $metadata = new WidgetMetadata(); + + return $metadata->getWidgetMetadata($categoryList, $widgetsList); + } + + /** * Get a combined report of the *.get API methods. */ public function get($idSite, $period, $date, $segment = false, $columns = false) @@ -555,7 +506,12 @@ class API extends \Piwik\Plugin\API if ($suggestedValuesCallbackRequiresTable) { $values = call_user_func($segmentFound['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table); } else { - $values = $this->getSegmentValuesFromVisitorLog($segmentName, $table); + // Cleanup data to return the top suggested (non empty) labels for this segment + $values = $table->getColumn($segmentName); + + // Select also flattened keys (custom variables "page" scope, page URLs for one visit, page titles for one visit) + $valuesBis = $table->getColumnsStartingWith($segmentName . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP); + $values = array_merge($values, $valuesBis); } $values = $this->getMostFrequentValues($values); @@ -600,9 +556,7 @@ class API extends \Piwik\Plugin\API // If you update this, also update flattenVisitorDetailsArray $segmentsNeedActionsInfo = array('visitConvertedGoalId', 'pageUrl', 'pageTitle', 'siteSearchKeyword', - 'entryPageTitle', 'entryPageUrl', 'exitPageTitle', 'exitPageUrl', - 'outlinkUrl', 'downloadUrl' - ); + 'entryPageTitle', 'entryPageUrl', 'exitPageTitle', 'exitPageUrl'); $isCustomVariablePage = stripos($segmentName, 'customVariablePage') !== false; $isEventSegment = stripos($segmentName, 'event') !== false; $isContentSegment = stripos($segmentName, 'content') !== false; diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php index d9d315b4c0..65b59a2051 100644 --- a/plugins/API/ProcessedReport.php +++ b/plugins/API/ProcessedReport.php @@ -23,9 +23,11 @@ use Piwik\Metrics\Formatter; use Piwik\Period; use Piwik\Piwik; use Piwik\Plugin\Report; +use Piwik\Plugin\Reports; use Piwik\Site; use Piwik\Timer; use Piwik\Url; +use Piwik\Category\Category; class ProcessedReport { @@ -166,77 +168,14 @@ class ProcessedReport $availableReports = array(); - foreach (Report::getAllReports() as $report) { + $reports = new Reports(); + foreach ($reports->getAllReports() as $report) { $report->configureReportMetadata($availableReports, $parameters); } - /** - * Triggered when gathering metadata for all available reports. - * - * Plugins that define new reports should use this event to make them available in via - * the metadata API. By doing so, the report will become available in scheduled reports - * as well as in the Piwik Mobile App. In fact, any third party app that uses the metadata - * API will automatically have access to the new report. - * - * @param string &$availableReports The list of available reports. Append to this list - * to make a report available. - * - * Every element of this array must contain the following - * information: - * - * - **category**: A translated string describing the report's category. - * - **name**: The translated display title of the report. - * - **module**: The plugin of the report. - * - **action**: The API method that serves the report. - * - * The following information is optional: - * - * - **dimension**: The report's [dimension](/guides/all-about-analytics-data#dimensions) if any. - * - **metrics**: An array mapping metric names with their display names. - * - **metricsDocumentation**: An array mapping metric names with their - * translated documentation. - * - **processedMetrics**: The array of metrics in the report that are - * calculated using existing metrics. Can be set to - * `false` if the report contains no processed - * metrics. - * - **order**: The order of the report in the list of reports - * with the same category. - * - * @param array $parameters Contains the values of the sites and period we are - * getting reports for. Some reports depend on this data. - * For example, Goals reports depend on the site IDs being - * requested. Contains the following information: - * - * - **idSites**: The array of site IDs we are getting reports for. - * - **period**: The period type, eg, `'day'`, `'week'`, `'month'`, - * `'year'`, `'range'`. - * - **date**: A string date within the period or a date range, eg, - * `'2013-01-01'` or `'2012-01-01,2013-01-01'`. - * - * TODO: put dimensions section in all about analytics data - * @deprecated since 2.5.0 Use Report Classes instead. - * @ignore - */ - Piwik::postEvent('API.getReportMetadata', array(&$availableReports, $parameters)); - - // TODO we can remove this one once we remove API.getReportMetadata event (except hideMetricsDoc) foreach ($availableReports as &$availableReport) { - // can be removed once we remove hook API.getReportMetadata - if (!isset($availableReport['metrics'])) { - $availableReport['metrics'] = Metrics::getDefaultMetrics(); - } - // can be removed once we remove hook API.getReportMetadata - if (!isset($availableReport['processedMetrics'])) { - $availableReport['processedMetrics'] = Metrics::getDefaultProcessedMetrics(); - } - - if ($hideMetricsDoc) // remove metric documentation if it's not wanted - { + if ($hideMetricsDoc) { unset($availableReport['metricsDocumentation']); - } else if (!isset($availableReport['metricsDocumentation'])) { - // set metric documentation to default if it's not set - // can be removed once we remove hook API.getReportMetadata - $availableReport['metricsDocumentation'] = Metrics::getDefaultMetricsDocumentation(); } } @@ -270,6 +209,9 @@ class ProcessedReport $columnsToRemove = $this->getColumnsToRemove(); foreach ($availableReports as &$availableReport) { + $availableReport['category'] = Piwik::translate($availableReport['category']); + $availableReport['subcategory'] = Piwik::translate($availableReport['subcategory']); + // Ensure all metrics have a translation $metrics = $availableReport['metrics']; $cleanedMetrics = array(); @@ -349,16 +291,8 @@ class ProcessedReport */ private static function sortReports($a, $b) { - static $order = null; - if (is_null($order)) { - $order = array(); - foreach (Report::$orderOfReports as $category) { - $order[] = Piwik::translate($category); - } - } - return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0 - ? (@$a['order'] < @$b['order'] ? -1 : 1) - : $category; + $reports = new Reports(); + return $reports->compareCategories($a['category'], $a['subcategory'], $a['order'], $b['category'], $b['subcategory'], $b['order']); } public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, diff --git a/plugins/API/Reports/Get.php b/plugins/API/Reports/Get.php index de087d0a86..61cded3856 100644 --- a/plugins/API/Reports/Get.php +++ b/plugins/API/Reports/Get.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\API\Reports; use Piwik\Piwik; use Piwik\Plugin\Report; +use Piwik\Plugin\Reports; class Get extends Report { @@ -29,7 +30,7 @@ class Get extends Report $this->module = 'API'; $this->action = 'get'; - $this->category = 'API'; + $this->categoryId = 'API'; $this->name = Piwik::translate('General_MainMetrics'); $this->documentation = ''; @@ -80,8 +81,9 @@ class Get extends Report */ private function getReportsToMerge() { + $reports = new Reports(); $result = array(); - foreach (Report::getAllReportClasses() as $reportClass) { + foreach ($reports->getAllReportClasses() as $reportClass) { if ($reportClass == 'Piwik\\Plugins\\API\\Reports\\Get') { continue; } diff --git a/plugins/API/SegmentMetadata.php b/plugins/API/SegmentMetadata.php new file mode 100644 index 0000000000..9d7e563bfb --- /dev/null +++ b/plugins/API/SegmentMetadata.php @@ -0,0 +1,167 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\API; + +use Piwik\Columns\Dimension; +use Piwik\Piwik; + +class SegmentMetadata +{ + public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true, $isAuthenticatedWithViewAccess) + { + $segments = array(); + + foreach (Dimension::getAllDimensions() as $dimension) { + foreach ($dimension->getSegments() as $segment) { + $segments[] = $segment->toArray(); + } + } + + /** + * Triggered when gathering all available segment dimensions. + * + * This event can be used to make new segment dimensions available. + * + * **Example** + * + * public function getSegmentsMetadata(&$segments, $idSites) + * { + * $segments[] = array( + * 'type' => 'dimension', + * 'category' => Piwik::translate('General_Visit'), + * 'name' => 'General_VisitorIP', + * 'segment' => 'visitIp', + * 'acceptedValues' => '13.54.122.1, etc.', + * 'sqlSegment' => 'log_visit.location_ip', + * 'sqlFilter' => array('Piwik\IP', 'P2N'), + * 'permission' => $isAuthenticatedWithViewAccess, + * ); + * } + * + * @param array &$dimensions The list of available segment dimensions. Append to this list to add + * new segments. Each element in this list must contain the + * following information: + * + * - **type**: Either `'metric'` or `'dimension'`. `'metric'` means + * the value is a numeric and `'dimension'` means it is + * a string. Also, `'metric'` values will be displayed + * under **Visit (metrics)** in the Segment Editor. + * - **category**: The segment category name. This can be an existing + * segment category visible in the segment editor. + * - **name**: The pretty name of the segment. Can be a translation token. + * - **segment**: The segment name, eg, `'visitIp'` or `'searches'`. + * - **acceptedValues**: A string describing one or two exacmple values, eg + * `'13.54.122.1, etc.'`. + * - **sqlSegment**: The table column this segment will segment by. + * For example, `'log_visit.location_ip'` for the + * **visitIp** segment. + * - **sqlFilter**: A PHP callback to apply to segment values before + * they are used in SQL. + * - **permission**: True if the current user has view access to this + * segment, false if otherwise. + * @param array $idSites The list of site IDs we're getting the available segments + * for. Some segments (such as Goal segments) depend on the + * site. + */ + Piwik::postEvent('API.getSegmentDimensionMetadata', array(&$segments, $idSites)); + + $segments[] = array( + 'type' => 'dimension', + 'category' => Piwik::translate('General_Visit'), + 'name' => 'General_UserId', + 'segment' => 'userId', + 'acceptedValues' => 'any non empty unique string identifying the user (such as an email address or a username).', + 'sqlSegment' => 'log_visit.user_id', + 'permission' => $isAuthenticatedWithViewAccess, + ); + + $segments[] = array( + 'type' => 'dimension', + 'category' => Piwik::translate('General_Visit'), + 'name' => 'General_VisitorID', + 'segment' => 'visitorId', + 'acceptedValues' => '34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()', + 'sqlSegment' => 'log_visit.idvisitor', + 'sqlFilterValue' => array('Piwik\Common', 'convertVisitorIdToBin'), + 'permission' => $isAuthenticatedWithViewAccess, + ); + + $segments[] = array( + 'type' => 'dimension', + 'category' => Piwik::translate('General_Visit'), + 'name' => Piwik::translate('General_Visit') . " ID", + 'segment' => 'visitId', + 'acceptedValues' => 'Any integer. ', + 'sqlSegment' => 'log_visit.idvisit', + 'permission' => $isAuthenticatedWithViewAccess, + ); + + $segments[] = array( + 'type' => 'metric', + 'category' => Piwik::translate('General_Visit'), + 'name' => 'General_VisitorIP', + 'segment' => 'visitIp', + 'acceptedValues' => '13.54.122.1. </code>Select IP ranges with notation: <code>visitIp>13.54.122.0;visitIp<13.54.122.255', + 'sqlSegment' => 'log_visit.location_ip', + 'sqlFilterValue' => array('Piwik\Network\IPUtils', 'stringToBinaryIP'), + 'permission' => $isAuthenticatedWithViewAccess, + ); + + foreach ($segments as &$segment) { + $segment['name'] = Piwik::translate($segment['name']); + $segment['category'] = Piwik::translate($segment['category']); + + if ($_hideImplementationData) { + unset($segment['sqlFilter']); + unset($segment['sqlFilterValue']); + unset($segment['sqlSegment']); + } + + if (isset($segment['suggestedValuesCallback']) + && !is_string($segment['suggestedValuesCallback']) + ) { + unset($segment['suggestedValuesCallback']); + } + } + + usort($segments, array($this, 'sortSegments')); + + return $segments; + } + + private function sortSegments($row1, $row2) + { + $customVarCategory = Piwik::translate('CustomVariables_CustomVariables'); + + $columns = array('type', 'category', 'name', 'segment'); + + foreach ($columns as $column) { + // Keep segments ordered alphabetically inside categories.. + $type = -1; + if ($column == 'name') $type = 1; + + $compare = $type * strcmp($row1[$column], $row2[$column]); + + // hack so that custom variables "page" are grouped together in the doc + if ($row1['category'] == $customVarCategory + && $row1['category'] == $row2['category'] + ) { + $compare = strcmp($row1['segment'], $row2['segment']); + return $compare; + } + + if ($compare != 0) { + return $compare; + } + } + + return $compare; + } + +}
\ No newline at end of file diff --git a/plugins/API/WidgetMetadata.php b/plugins/API/WidgetMetadata.php new file mode 100644 index 0000000000..ef12800a39 --- /dev/null +++ b/plugins/API/WidgetMetadata.php @@ -0,0 +1,282 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\API; + +use Piwik\Category\CategoryList; +use Piwik\Piwik; +use Piwik\Report\ReportWidgetConfig; +use Piwik\Category\Category; +use Piwik\Category\Subcategory; +use Piwik\Widget\WidgetContainerConfig; +use Piwik\Widget\WidgetConfig; +use Piwik\Widget\WidgetsList; + +class WidgetMetadata +{ + public function getPagesMetadata(CategoryList $categoryList, WidgetsList $widgetsList) + { + $this->createMissingCategoriesAndSubcategories($categoryList, $widgetsList->getWidgetConfigs()); + + return $this->buildPagesMetadata($categoryList, $widgetsList); + } + + public function getWidgetMetadata(CategoryList $categoryList, WidgetsList $widgetsList) + { + $this->createMissingCategoriesAndSubcategories($categoryList, $widgetsList->getWidgetConfigs()); + + $flat = array(); + + foreach ($widgetsList->getWidgetConfigs() as $widgetConfig) { + + /** @var WidgetConfig[] $widgets */ + $widgets = array($widgetConfig); + if ($widgetConfig instanceof WidgetContainerConfig) { + // so far we go only one level down, in theory these widgetConfigs could have again containers containing configs + $widgets = array_merge($widgets, $widgetConfig->getWidgetConfigs()); + } + + foreach ($widgets as $widget) { + // make sure to include only widgetizable widgets + if (!$widget->isWidgetizeable() || !$widget->getName()) { + continue; + } + + $flat[] = $this->buildWidgetMetadata($widget, $categoryList); + } + } + + usort($flat, array($this, 'sortWidgets')); + + return $flat; + } + + /** + * @param WidgetConfig $widget + * @param CategoryList|null $categoryList If null, no category information will be added to the widgets in first + * level (they will be added to nested widgets as potentially needed eg for + * widgets in ByDimensionView where they are needed to build the left menu) + * @return array + */ + public function buildWidgetMetadata(WidgetConfig $widget, $categoryList = null) + { + $item = array( + 'name' => Piwik::translate($widget->getName()) + ); + + if (isset($categoryList)) { + $category = $categoryList->getCategory($widget->getCategoryId()); + $subcategory = $category ? $category->getSubcategory($widget->getSubcategoryId()) : null; + + $item['category'] = $this->buildCategoryMetadata($category); + $item['subcategory'] = $this->buildSubcategoryMetadata($subcategory); + } + + $item['module'] = $widget->getModule(); + $item['action'] = $widget->getAction(); + $item['order'] = $widget->getOrder(); + $item['parameters'] = $widget->getParameters(); + $item['uniqueId'] = $widget->getUniqueId(); + + $middleware = $widget->getMiddlewareParameters(); + + if (!empty($middleware)) { + $item['middlewareParameters'] = $middleware; + } + + if ($widget instanceof ReportWidgetConfig) { + $item['viewDataTable'] = $widget->getViewDataTable(); + $item['isReport'] = true; + } + + if ($widget instanceof WidgetContainerConfig) { + $item['layout'] = $widget->getLayout(); + $item['isContainer'] = true; + + // we do not want to create categories to the inital categoryList. Otherwise we'd maybe display more pages + // etc. + $subCategoryList = new CategoryList(); + $this->createMissingCategoriesAndSubcategories($subCategoryList, $widget->getWidgetConfigs()); + + $children = array(); + foreach ($widget->getWidgetConfigs() as $widgetConfig) { + $children[] = $this->buildWidgetMetadata($widgetConfig, $subCategoryList); + } + $item['widgets'] = $children; + } + + return $item; + } + + private function sortWidgets($widgetA, $widgetB) { + $orderA = $widgetA['category']['order']; + $orderB = $widgetB['category']['order']; + + if ($orderA === $orderB) { + if (!empty($widgetA['subcategory']['order']) && !empty($widgetB['subcategory']['order'])) { + + $subOrderA = $widgetA['subcategory']['order']; + $subOrderB = $widgetB['subcategory']['order']; + + if ($subOrderA === $subOrderB) { + return 0; + } + + return $subOrderA > $subOrderB ? 1 : -1; + + } elseif (!empty($orderA)) { + + return 1; + } + + return -1; + } + + return $orderA > $orderB ? 1 : -1; + } + + /** + * @param Category|null $category + * @return array + */ + private function buildCategoryMetadata($category) + { + if (!isset($category)) { + return null; + } + + return array( + 'id' => (string) $category->getId(), + 'name' => Piwik::translate($category->getId()), + 'order' => $category->getOrder(), + ); + } + + /** + * @param Subcategory|null $subcategory + * @return array + */ + private function buildSubcategoryMetadata($subcategory) + { + if (!isset($subcategory)) { + return null; + } + + return array( + 'id' => (string) $subcategory->getId(), + 'name' => Piwik::translate($subcategory->getName()), + 'order' => $subcategory->getOrder(), + ); + } + + /** + * @param CategoryList $categoryList + * @param WidgetConfig[] $widgetConfigs + */ + private function createMissingCategoriesAndSubcategories($categoryList, $widgetConfigs) + { + // move reports into categories/subcategories and create missing ones if needed + foreach ($widgetConfigs as $widgetConfig) { + $categoryId = $widgetConfig->getCategoryId(); + $subcategoryId = $widgetConfig->getSubcategoryId(); + + if (!$categoryId) { + continue; + } + + if ($widgetConfig instanceof WidgetContainerConfig && !$widgetConfig->getWidgetConfigs()) { + // if a container does not contain any widgets, ignore it + continue; + } + + if (!$categoryList->hasCategory($categoryId)) { + $categoryList->addCategory($this->createCategory($categoryId)); + } + + if (!$subcategoryId) { + continue; + } + + $category = $categoryList->getCategory($categoryId); + + if (!$category->hasSubcategory($subcategoryId)) { + $category->addSubcategory($this->createSubcategory($categoryId, $subcategoryId)); + } + } + } + + private function createCategory($categoryId) + { + $category = new Category(); + $category->setId($categoryId); + return $category; + } + + private function createSubcategory($categoryId, $subcategoryId) + { + $subcategory = new Subcategory(); + $subcategory->setCategoryId($categoryId); + $subcategory->setId($subcategoryId); + return $subcategory; + } + + /** + * @param CategoryList $categoryList + * @param WidgetsList $widgetsList + * @return array + */ + private function buildPagesMetadata(CategoryList $categoryList, WidgetsList $widgetsList) + { + $pages = array(); + + $widgets = array(); + foreach ($widgetsList->getWidgetConfigs() as $config) { + $pageId = $this->buildPageId($config->getCategoryId(), $config->getSubcategoryId()); + + if (!isset($widgets[$pageId])) { + $widgets[$pageId] = array(); + } + + $widgets[$pageId][] = $config; + } + + foreach ($categoryList->getCategories() as $category) { + foreach ($category->getSubcategories() as $subcategory) { + $pageId = $this->buildPageId($category->getId(), $subcategory->getId()); + + if (!empty($widgets[$pageId])) { + $pages[] = $this->buildPageMetadata($category, $subcategory, $widgets[$pageId]); + } + } + } + + return $pages; + } + + private function buildPageId($categoryId, $subcategoryId) + { + return $categoryId . '.' . $subcategoryId; + } + + public function buildPageMetadata(Category $category, Subcategory $subcategory, $widgetConfigs) + { + $ca = array( + 'uniqueId' => $this->buildPageId($category->getId(), $subcategory->getId()), + 'category' => $this->buildCategoryMetadata($category), + 'subcategory' => $this->buildSubcategoryMetadata($subcategory), + 'widgets' => array() + ); + + foreach ($widgetConfigs as $config) { + $ca['widgets'][] = $this->buildWidgetMetadata($config); + } + + return $ca; + } + +}
\ No newline at end of file diff --git a/plugins/API/tests/Unit/WidgetMetadataTest.php b/plugins/API/tests/Unit/WidgetMetadataTest.php new file mode 100644 index 0000000000..bb4379a010 --- /dev/null +++ b/plugins/API/tests/Unit/WidgetMetadataTest.php @@ -0,0 +1,278 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\API\tests\Unit; + +use Piwik\Category\Category; +use Piwik\Category\CategoryList; +use Piwik\Category\Subcategory; +use Piwik\DataTable; +use Piwik\Plugins\API\Renderer\Console; +use Piwik\Plugins\API\WidgetMetadata; +use Piwik\Plugins\CoreHome\CoreHome; +use Piwik\Report\ReportWidgetConfig; +use Piwik\Widget\WidgetConfig; +use Piwik\Widget\WidgetContainerConfig; + +/** + * @group Widget + * @group Widgets + * @group WidgetMetadata + * @group WidgetMetadataTest + */ +class WidgetMetadataTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var WidgetMetadata + */ + private $metadata; + + public function setUp() + { + $this->metadata = new WidgetMetadata(); + } + + public function test_buildWidgetMetadata_ShouldGenerateMetadata() + { + $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId'); + $list = $this->createCategoryList(array('CategoryId' => array('SubcategoryId'))); + $metadata = $this->metadata->buildWidgetMetadata($config, $list); + + $this->assertEquals(array( + 'name' => 'Test', + 'category' => array( + 'id' => 'CategoryId', + 'name' => 'CategoryId', + 'order' => 99, + ), + 'subcategory' => array( + 'id' => 'SubcategoryId', + 'name' => 'SubcategoryIdName', + 'order' => 99, + ), + 'module' => 'CoreHome', + 'action' => 'render', + 'order' => 99, + 'parameters' => array ( + 'module' => 'CoreHome', + 'action' => 'render' + ), + 'uniqueId' => 'widgetCoreHomerender' + ), $metadata); + } + + public function test_buildWidgetMetadata_ShouldSetCategoryAndSubcategoryToNull_IfBothGivenButNotExistInList() + { + $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId'); + $list = $this->createCategoryList(); + $metadata = $this->metadata->buildWidgetMetadata($config, $list); + + $this->assertNull($metadata['category']); + $this->assertNull($metadata['subcategory']); + } + + public function test_buildWidgetMetadata_ShouldSetSubcategoryToNull_IfCategoryGivenInListButSubcategoryNot() + { + $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId'); + $list = $this->createCategoryList(array('CategoryId' => array())); + $metadata = $this->metadata->buildWidgetMetadata($config, $list); + + $this->assertSame(array( + 'id' => 'CategoryId', + 'name' => 'CategoryId', + 'order' => 99, + ), $metadata['category']); + $this->assertNull($metadata['subcategory']); + } + + public function test_buildWidgetMetadata_ShouldNotAddCategoryAndSubcategoryToNull_IfNoCategoryListGiven() + { + $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId'); + $metadata = $this->metadata->buildWidgetMetadata($config); + + $this->assertArrayNotHasKey('category', $metadata); + $this->assertArrayNotHasKey('subcategory', $metadata); + } + + public function test_buildWidgetMetadata_ShouldAddOptionalMiddlewareParameters() + { + $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId'); + $config->setMiddlewareParameters(array('module' => 'Goals', 'action' => 'hasAnyConversions')); + $metadata = $this->metadata->buildWidgetMetadata($config); + + $this->assertSame(array('module' => 'Goals', 'action' => 'hasAnyConversions'), $metadata['middlewareParameters']); + } + + public function test_buildWidgetMetadata_ShouldAddReportInformtion_IfReportWidgetConfigGiven() + { + $config = new ReportWidgetConfig(); + $config->setDefaultViewDataTable('graph'); + $metadata = $this->metadata->buildWidgetMetadata($config); + + $this->assertSame('graph', $metadata['viewDataTable']); + $this->assertTrue($metadata['isReport']); + } + + public function test_buildWidgetMetadata_ShouldAddContainerInformtion_IfWidgetContainerConfigGiven() + { + $config = new WidgetContainerConfig(); + $config->setLayout('ByDimension'); + $config->addWidgetConfig($this->createWidgetConfig('NestedName1', 'NestedCategory1', 'NestedSubcategory1')); + $config->addWidgetConfig($this->createWidgetConfig('NestedName2', 'NestedCategory2', 'NestedSubcategory2')); + $metadata = $this->metadata->buildWidgetMetadata($config); + + $this->assertSame('ByDimension', $metadata['layout']); + $this->assertTrue($metadata['isContainer']); + $this->assertCount(2, $metadata['widgets']); + + $widget1 = $metadata['widgets'][0]; + $widget2 = $metadata['widgets'][1]; + $this->assertSame(array( + 'name' => 'NestedName1', + 'category' => array ( + 'id' => 'NestedCategory1', + 'name' => 'NestedCategory1', + 'order' => 99 + ), + 'subcategory' => array ( + 'id' => 'NestedSubcategory1', + 'name' => 'NestedSubcategory1', + 'order' => 99 + ), + 'module' => 'CoreHome', + 'action' => 'render', + 'order' => 99, + 'parameters' => array ( + 'module' => 'CoreHome', + 'action' => 'render', + ), + 'uniqueId' => 'widgetCoreHomerender' + ), $widget1); + $this->assertSame(array( + 'name' => 'NestedName2', + 'category' => array ( + 'id' => 'NestedCategory2', + 'name' => 'NestedCategory2', + 'order' => 99 + ), + 'subcategory' => array ( + 'id' => 'NestedSubcategory2', + 'name' => 'NestedSubcategory2', + 'order' => 99 + ), + 'module' => 'CoreHome', + 'action' => 'render', + 'order' => 99, + 'parameters' => array ( + 'module' => 'CoreHome', + 'action' => 'render', + ), + 'uniqueId' => 'widgetCoreHomerender' + ), $widget2); + } + + public function test_buildPageMetadata_ShouldAddContainerInformtion_IfWidgetContainerConfigGiven() + { + $config = new WidgetContainerConfig(); + $config->setLayout('ByDimension'); + + $widgets = array( + $this->createWidgetConfig('NestedName1', 'NestedCategory1', 'NestedSubcategory1'), + $this->createWidgetConfig('NestedName2', 'NestedCategory2', 'NestedSubcategory1'), + ); + + $category = $this->createCategory('NestedCategory1'); + $subcategory = $this->createSubcategory('NestedCategory1' ,'NestedSubcategory1'); + + $metadata = $this->metadata->buildPageMetadata($category, $subcategory, $widgets); + + $this->assertSame(array( + 'uniqueId' => 'NestedCategory1.NestedSubcategory1', + 'category' => array ( + 'id' => 'NestedCategory1', + 'name' => 'NestedCategory1', + 'order' => 99, + ), + 'subcategory' => array ( + 'id' => 'NestedSubcategory1', + 'name' => 'NestedSubcategory1Name', + 'order' => 99, + ), + 'widgets' => array ( + 0 => array ( // widgets should not have category / subcategory again, it's already present above + 'name' => 'NestedName1', + 'module' => 'CoreHome', + 'action' => 'render', + 'order' => 99, + 'parameters' => array ( + 'module' => 'CoreHome', + 'action' => 'render', + ), + 'uniqueId' => 'widgetCoreHomerender', + ), array ( + 'name' => 'NestedName2', + 'module' => 'CoreHome', + 'action' => 'render', + 'order' => 99, + 'parameters' => array ( + 'module' => 'CoreHome', + 'action' => 'render', + ), + 'uniqueId' => 'widgetCoreHomerender' + ) + ) + ), $metadata); + } + + private function createWidgetConfig($name, $categoryId, $subcategoryId = '') + { + $widgetConfig = new WidgetConfig(); + $widgetConfig->setName($name); + $widgetConfig->setCategoryId($categoryId); + $widgetConfig->setSubcategoryId($subcategoryId); + $widgetConfig->setModule('CoreHome'); + $widgetConfig->setAction('render'); + + return $widgetConfig; + } + + private function createCategoryList($categories = array()) + { + $list = new CategoryList(); + + foreach ($categories as $categoryId => $subcategoryIds) { + $category = $this->createCategory($categoryId); + $list->addCategory($category); + + foreach ($subcategoryIds as $subcategoryId) { + $subcategory = $this->createSubcategory($categoryId, $subcategoryId); + $category->addSubcategory($subcategory); + } + } + + return $list; + } + + private function createSubcategory($categoryId, $subcategoryId) + { + $subcategory = new Subcategory(); + $subcategory->setCategoryId($categoryId); + $subcategory->setId($subcategoryId); + $subcategory->setName($subcategoryId . 'Name'); + + return $subcategory; + } + + private function createCategory($categoryId) + { + $category = new Category(); + $category->setId($categoryId); + return $category; + } + +} diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php index 891400e122..e475f4240c 100644 --- a/plugins/Actions/API.php +++ b/plugins/Actions/API.php @@ -22,6 +22,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; use Piwik\Plugins\CustomVariables\API as APICustomVariables; use Piwik\Plugins\Actions\Actions\ActionSiteSearch; +use Piwik\Plugin\Reports; use Piwik\Tracker\Action; use Piwik\Tracker\PageUrl; @@ -55,7 +56,7 @@ class API extends \Piwik\Plugin\API { Piwik::checkUserHasViewAccess($idSite); - $report = Report::factory("Actions", "get"); + $report = Reports::factory("Actions", "get"); $archive = Archive::build($idSite, $period, $date, $segment); $requestedColumns = Piwik::getArrayFromApiParameter($columns); diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 557e4c77b8..bca4396271 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Actions; use Piwik\ArchiveProcessor; use Piwik\Common; use Piwik\Db; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; use Piwik\Site; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; diff --git a/plugins/Actions/Categories/DownloadsSubcategory.php b/plugins/Actions/Categories/DownloadsSubcategory.php new file mode 100644 index 0000000000..ac432b11d1 --- /dev/null +++ b/plugins/Actions/Categories/DownloadsSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class DownloadsSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'General_Downloads'; + protected $order = 35; + +} diff --git a/plugins/Actions/Categories/EntryPagesSubcategory.php b/plugins/Actions/Categories/EntryPagesSubcategory.php new file mode 100644 index 0000000000..d4ee657b79 --- /dev/null +++ b/plugins/Actions/Categories/EntryPagesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class EntryPagesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'Actions_SubmenuPagesEntry'; + protected $order = 10; + +} diff --git a/plugins/Actions/Categories/ExitPagesSubcategory.php b/plugins/Actions/Categories/ExitPagesSubcategory.php new file mode 100644 index 0000000000..b1a5a636c6 --- /dev/null +++ b/plugins/Actions/Categories/ExitPagesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class ExitPagesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'Actions_SubmenuPagesExit'; + protected $order = 15; + +} diff --git a/plugins/Actions/Categories/OutlinksSubcategory.php b/plugins/Actions/Categories/OutlinksSubcategory.php new file mode 100644 index 0000000000..975ca9003e --- /dev/null +++ b/plugins/Actions/Categories/OutlinksSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class OutlinksSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'General_Outlinks'; + protected $order = 30; + +} diff --git a/plugins/Actions/Categories/PageTitlesSubcategory.php b/plugins/Actions/Categories/PageTitlesSubcategory.php new file mode 100644 index 0000000000..1a88b6dbd2 --- /dev/null +++ b/plugins/Actions/Categories/PageTitlesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class PageTitlesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'Actions_SubmenuPageTitles'; + protected $order = 20; + +} diff --git a/plugins/Actions/Categories/PagesSubcategory.php b/plugins/Actions/Categories/PagesSubcategory.php new file mode 100644 index 0000000000..5d407859b1 --- /dev/null +++ b/plugins/Actions/Categories/PagesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class PagesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'General_Pages'; + protected $order = 5; + +} diff --git a/plugins/Actions/Categories/SiteSearchSubcategory.php b/plugins/Actions/Categories/SiteSearchSubcategory.php new file mode 100644 index 0000000000..e915803679 --- /dev/null +++ b/plugins/Actions/Categories/SiteSearchSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Actions\Categories; + +use Piwik\Category\Subcategory; + +class SiteSearchSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'Actions_SubmenuSitesearch'; + protected $order = 25; + +} diff --git a/plugins/Actions/Controller.php b/plugins/Actions/Controller.php deleted file mode 100644 index 51c7d7ba3a..0000000000 --- a/plugins/Actions/Controller.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Actions; - -use Piwik\Plugin\Report; -use Piwik\View; - -/** - * Actions controller - * - */ -class Controller extends \Piwik\Plugin\Controller -{ - // - // Actions that render whole pages - // - - public function indexSiteSearch() - { - $view = new View('@Actions/indexSiteSearch'); - - $keyword = Report::factory($this->pluginName, 'getSiteSearchKeywords'); - $noResult = Report::factory($this->pluginName, 'getSiteSearchNoResultKeywords'); - $pageUrls = Report::factory($this->pluginName, 'getPageUrlsFollowingSiteSearch'); - - $view->keywords = $keyword->render(); - $view->noResultKeywords = $noResult->render(); - $view->pagesUrlsFollowingSiteSearch = $pageUrls->render(); - - $categoryTrackingEnabled = Actions::isCustomVariablesPluginsEnabled(); - if ($categoryTrackingEnabled) { - $categories = Report::factory($this->pluginName, 'getSiteSearchCategories'); - $view->categories = $categories->render(); - } - - return $view->render(); - } - -} diff --git a/plugins/Actions/Menu.php b/plugins/Actions/Menu.php deleted file mode 100644 index 9a7424b4e9..0000000000 --- a/plugins/Actions/Menu.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Actions; - -use Piwik\Common; -use Piwik\Menu\MenuReporting; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addActionsItem('', $this->urlForAction('menuGetPageUrls'), 15); - - $idSite = Common::getRequestVar('idSite', 0, 'int'); - $idSites = Common::getRequestVar('idSites', '', 'string'); - - $actions = new Actions(); - if ($actions->isSiteSearchEnabled($idSites, $idSite)) { - $menu->addActionsItem('Actions_SubmenuSitesearch', $this->urlForAction('indexSiteSearch'), 5); - } - } - -} diff --git a/plugins/Actions/Reports/Base.php b/plugins/Actions/Reports/Base.php index 45c9c0af9c..20e35593d8 100644 --- a/plugins/Actions/Reports/Base.php +++ b/plugins/Actions/Reports/Base.php @@ -21,7 +21,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_Actions'; + $this->categoryId = 'General_Actions'; $this->processedMetrics = false; $this->recursiveLabelSeparator = '/'; } diff --git a/plugins/Actions/Reports/GetDownloads.php b/plugins/Actions/Reports/GetDownloads.php index f168cba878..92975b969e 100644 --- a/plugins/Actions/Reports/GetDownloads.php +++ b/plugins/Actions/Reports/GetDownloads.php @@ -26,8 +26,7 @@ class GetDownloads extends Base $this->actionToLoadSubTables = $this->action; $this->order = 9; - $this->menuTitle = 'General_Downloads'; - $this->widgetTitle = 'General_Downloads'; + $this->subcategoryId = 'General_Downloads'; } public function getMetrics() diff --git a/plugins/Actions/Reports/GetEntryPageTitles.php b/plugins/Actions/Reports/GetEntryPageTitles.php index 7c029f4b02..9df1d74450 100644 --- a/plugins/Actions/Reports/GetEntryPageTitles.php +++ b/plugins/Actions/Reports/GetEntryPageTitles.php @@ -15,6 +15,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; +use Piwik\Plugin\Reports; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetEntryPageTitles extends Base { @@ -35,8 +38,11 @@ class GetEntryPageTitles extends Base ); $this->order = 6; $this->actionToLoadSubTables = $this->action; + } - $this->widgetTitle = 'Actions_WidgetEntryPageTitles'; + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig($factory->createWidget()->setName('Actions_WidgetEntryPageTitles')); } public function getProcessedMetrics() @@ -79,8 +85,8 @@ class GetEntryPageTitles extends Base public function getRelatedReports() { return array( - self::factory('Actions', 'getPageTitles'), - self::factory('Actions', 'getEntryPageUrls') + Reports::factory('Actions', 'getPageTitles'), + Reports::factory('Actions', 'getEntryPageUrls') ); } } diff --git a/plugins/Actions/Reports/GetEntryPageUrls.php b/plugins/Actions/Reports/GetEntryPageUrls.php index f3795db099..c934c7ab10 100644 --- a/plugins/Actions/Reports/GetEntryPageUrls.php +++ b/plugins/Actions/Reports/GetEntryPageUrls.php @@ -17,6 +17,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; +use Piwik\Plugin\Reports; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetEntryPageUrls extends Base { @@ -40,8 +43,7 @@ class GetEntryPageUrls extends Base $this->actionToLoadSubTables = $this->action; - $this->menuTitle = 'Actions_SubmenuPagesEntry'; - $this->widgetTitle = 'Actions_WidgetPagesEntry'; + $this->subcategoryId = 'Actions_SubmenuPagesEntry'; } public function getProcessedMetrics() @@ -83,7 +85,7 @@ class GetEntryPageUrls extends Base public function getRelatedReports() { return array( - self::factory('Actions', 'getEntryPageTitles'), + Reports::factory('Actions', 'getEntryPageTitles'), ); } } diff --git a/plugins/Actions/Reports/GetExitPageTitles.php b/plugins/Actions/Reports/GetExitPageTitles.php index 6cc5b1f320..53f235209d 100644 --- a/plugins/Actions/Reports/GetExitPageTitles.php +++ b/plugins/Actions/Reports/GetExitPageTitles.php @@ -15,6 +15,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; +use Piwik\Plugin\Reports; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetExitPageTitles extends Base { @@ -37,8 +40,13 @@ class GetExitPageTitles extends Base $this->order = 7; $this->actionToLoadSubTables = $this->action; + } - $this->widgetTitle = 'Actions_WidgetExitPageTitles'; + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + // we have to do it manually since it's only done automatically if a subcategoryId is specified, + // we do not set a subcategoryId since this report is not supposed to be shown in the UI + $widgetsList->addWidgetConfig($factory->createWidget()); } public function getProcessedMetrics() @@ -86,8 +94,8 @@ class GetExitPageTitles extends Base public function getRelatedReports() { return array( - self::factory('Actions', 'getPageTitles'), - self::factory('Actions', 'getExitPageUrls'), + Reports::factory('Actions', 'getPageTitles'), + Reports::factory('Actions', 'getExitPageUrls'), ); } } diff --git a/plugins/Actions/Reports/GetExitPageUrls.php b/plugins/Actions/Reports/GetExitPageUrls.php index 2ef5d7a957..f46ac478cd 100644 --- a/plugins/Actions/Reports/GetExitPageUrls.php +++ b/plugins/Actions/Reports/GetExitPageUrls.php @@ -17,6 +17,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; +use Piwik\Plugin\Reports; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetExitPageUrls extends Base { @@ -40,8 +43,7 @@ class GetExitPageUrls extends Base $this->order = 4; - $this->menuTitle = 'Actions_SubmenuPagesExit'; - $this->widgetTitle = 'Actions_WidgetPagesExit'; + $this->subcategoryId = 'Actions_SubmenuPagesExit'; } public function getProcessedMetrics() @@ -97,7 +99,7 @@ class GetExitPageUrls extends Base public function getRelatedReports() { return array( - self::factory('Actions', 'getExitPageTitles'), + Reports::factory('Actions', 'getExitPageTitles'), ); } diff --git a/plugins/Actions/Reports/GetOutlinks.php b/plugins/Actions/Reports/GetOutlinks.php index adf21c572c..fb5e13e831 100644 --- a/plugins/Actions/Reports/GetOutlinks.php +++ b/plugins/Actions/Reports/GetOutlinks.php @@ -29,8 +29,7 @@ class GetOutlinks extends Base $this->actionToLoadSubTables = $this->action; - $this->menuTitle = 'General_Outlinks'; - $this->widgetTitle = 'General_Outlinks'; + $this->subcategoryId = 'General_Outlinks'; } public function getMetrics() diff --git a/plugins/Actions/Reports/GetPageTitles.php b/plugins/Actions/Reports/GetPageTitles.php index 8f7e193f9b..0c70086611 100644 --- a/plugins/Actions/Reports/GetPageTitles.php +++ b/plugins/Actions/Reports/GetPageTitles.php @@ -17,6 +17,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; +use Piwik\Plugin\Reports; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetPageTitles extends Base { @@ -40,8 +43,7 @@ class GetPageTitles extends Base $this->actionToLoadSubTables = $this->action; - $this->menuTitle = 'Actions_SubmenuPageTitles'; - $this->widgetTitle = 'Actions_WidgetPageTitles'; + $this->subcategoryId = 'Actions_SubmenuPageTitles'; } public function getMetrics() @@ -81,8 +83,8 @@ class GetPageTitles extends Base public function getRelatedReports() { return array( - self::factory('Actions', 'getEntryPageTitles'), - self::factory('Actions', 'getExitPageTitles'), + Reports::factory('Actions', 'getEntryPageTitles'), + Reports::factory('Actions', 'getExitPageTitles'), ); } } diff --git a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php index 657e88211d..3d50ba4f95 100644 --- a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php +++ b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php @@ -15,6 +15,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; +use Piwik\Plugin\Reports; class GetPageTitlesFollowingSiteSearch extends SiteSearchBase { @@ -32,7 +33,8 @@ class GetPageTitlesFollowingSiteSearch extends SiteSearchBase new AveragePageGenerationTime() ); $this->order = 19; - $this->widgetTitle = 'Actions_WidgetPageTitlesFollowingSearch'; + + $this->subcategoryId = 'Actions_SubmenuSitesearch'; } public function configureView(ViewDataTable $view) @@ -80,7 +82,7 @@ class GetPageTitlesFollowingSiteSearch extends SiteSearchBase public function getRelatedReports() { return array( - self::factory('Actions', 'getPageUrlsFollowingSiteSearch'), + Reports::factory('Actions', 'getPageUrlsFollowingSiteSearch'), ); } } diff --git a/plugins/Actions/Reports/GetPageUrls.php b/plugins/Actions/Reports/GetPageUrls.php index 0af899ef0e..2c850a5339 100644 --- a/plugins/Actions/Reports/GetPageUrls.php +++ b/plugins/Actions/Reports/GetPageUrls.php @@ -15,6 +15,8 @@ use Piwik\Plugins\Actions\Columns\Metrics\BounceRate; use Piwik\Plugins\Actions\Columns\PageUrl; use Piwik\Plugins\Actions\Columns\Metrics\ExitRate; use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetPageUrls extends Base { @@ -37,10 +39,13 @@ class GetPageUrls extends Base new AveragePageGenerationTime() ); - $this->segmentSql = 'log_visit.visit_entry_idaction_url'; + $this->segmentSql = 'log_visit.visit_entry_idaction_url'; + $this->subcategoryId = 'General_Pages'; + } - $this->menuTitle = 'General_Pages'; - $this->widgetTitle = 'General_Pages'; + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig($factory->createWidget()->setName($this->subcategoryId)); } public function getMetrics() diff --git a/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php index 2d8ba1a3a2..063d3855b5 100644 --- a/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php +++ b/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Actions\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Actions\Columns\DestinationPage; +use Piwik\Plugin\Reports; class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch { @@ -20,8 +21,9 @@ class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch $this->dimension = new DestinationPage(); $this->name = Piwik::translate('Actions_WidgetPageUrlsFollowingSearch'); $this->documentation = Piwik::translate('Actions_SiteSearchFollowingPagesDoc') . '<br/>' . Piwik::translate('General_UsePlusMinusIconsDocumentation'); - $this->order = 18; - $this->widgetTitle = 'Actions_WidgetPageUrlsFollowingSearch'; + $this->order = 16; + + $this->subcategoryId = 'Actions_SubmenuSitesearch'; } public function configureView(ViewDataTable $view) @@ -34,7 +36,7 @@ class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch public function getRelatedReports() { return array( - self::factory('Actions', 'getPageTitlesFollowingSiteSearch'), + Reports::factory('Actions', 'getPageTitlesFollowingSiteSearch'), ); } } diff --git a/plugins/Actions/Reports/GetSiteSearchCategories.php b/plugins/Actions/Reports/GetSiteSearchCategories.php index 6d41e3c334..5388ff9198 100644 --- a/plugins/Actions/Reports/GetSiteSearchCategories.php +++ b/plugins/Actions/Reports/GetSiteSearchCategories.php @@ -23,8 +23,9 @@ class GetSiteSearchCategories extends SiteSearchBase $this->name = Piwik::translate('Actions_WidgetSearchCategories'); $this->documentation = Piwik::translate('Actions_SiteSearchCategories1') . '<br/>' . Piwik::translate('Actions_SiteSearchCategories2'); $this->metrics = array('nb_visits', 'nb_pages_per_search', 'exit_rate'); - $this->order = 17; - $this->widgetTitle = 'Actions_WidgetSearchCategories'; + $this->order = 20; + + $this->subcategoryId = 'Actions_SubmenuSitesearch'; } protected function isEnabledForIdSites($idSites, $idSite) diff --git a/plugins/Actions/Reports/GetSiteSearchKeywords.php b/plugins/Actions/Reports/GetSiteSearchKeywords.php index d88684cd15..dc4b5e2d16 100644 --- a/plugins/Actions/Reports/GetSiteSearchKeywords.php +++ b/plugins/Actions/Reports/GetSiteSearchKeywords.php @@ -33,7 +33,7 @@ class GetSiteSearchKeywords extends SiteSearchBase new AveragePageGenerationTime() ); $this->order = 15; - $this->widgetTitle = 'Actions_WidgetSearchKeywords'; + $this->subcategoryId = 'Actions_SubmenuSitesearch'; } public function getMetrics() diff --git a/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php b/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php index 94b19e8566..c1c01ca504 100644 --- a/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php +++ b/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php @@ -31,8 +31,9 @@ class GetSiteSearchNoResultKeywords extends SiteSearchBase new ExitRate(), new AveragePageGenerationTime() ); - $this->order = 16; - $this->widgetTitle = 'Actions_WidgetSearchNoResultKeywords'; + $this->order = 18; + + $this->subcategoryId = 'Actions_SubmenuSitesearch'; } public function getMetrics() diff --git a/plugins/Actions/Reports/SiteSearchBase.php b/plugins/Actions/Reports/SiteSearchBase.php index e30a2243d8..d4715e04e6 100644 --- a/plugins/Actions/Reports/SiteSearchBase.php +++ b/plugins/Actions/Reports/SiteSearchBase.php @@ -18,7 +18,7 @@ abstract class SiteSearchBase extends Base protected function init() { parent::init(); - $this->category = 'Actions_SubmenuSitesearch'; + $this->categoryId = 'General_Actions'; } public function isEnabled() diff --git a/plugins/Actions/templates/indexSiteSearch.twig b/plugins/Actions/templates/indexSiteSearch.twig deleted file mode 100644 index 8d9eaa0909..0000000000 --- a/plugins/Actions/templates/indexSiteSearch.twig +++ /dev/null @@ -1,21 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'Actions_WidgetSearchKeywords'|translate }}</h2> - {{ keywords|raw }} - - <h2 piwik-enriched-headline>{{ 'Actions_WidgetSearchNoResultKeywords'|translate }}</h2> - {{ noResultKeywords|raw }} - - {% if categories is defined %} - <h2 piwik-enriched-headline>{{ 'Actions_WidgetSearchCategories'|translate }}</h2> - {{ categories|raw }} - {% endif %} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'Actions_WidgetPageUrlsFollowingSearch'|translate }}</h2> - {{ pagesUrlsFollowingSiteSearch|raw }} - </div> - -</div> diff --git a/plugins/Contents/Categories/ContentsSubcategory.php b/plugins/Contents/Categories/ContentsSubcategory.php new file mode 100644 index 0000000000..735d376e4f --- /dev/null +++ b/plugins/Contents/Categories/ContentsSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Contents\Categories; + +use Piwik\Category\Subcategory; + +class ContentsSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'Contents_Contents'; + protected $order = 45; + +} diff --git a/plugins/Contents/Contents.php b/plugins/Contents/Contents.php index 10536930af..49df9a730b 100644 --- a/plugins/Contents/Contents.php +++ b/plugins/Contents/Contents.php @@ -18,7 +18,7 @@ class Contents extends \Piwik\Plugin return array( 'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations', 'AssetManager.getJavaScriptFiles' => 'getJsFiles', - 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', + 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles' ); } diff --git a/plugins/Contents/Controller.php b/plugins/Contents/Controller.php deleted file mode 100644 index a17ec3fdce..0000000000 --- a/plugins/Contents/Controller.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Contents; - -use Piwik\Plugin\Report; -use Piwik\View; - -class Controller extends \Piwik\Plugin\Controller -{ - - public function index() - { - $reportsView = new View\ReportsByDimension('Contents'); - - /** @var \Piwik\Plugin\Report[] $reports */ - $contentNames = Report::factory($this->pluginName, 'getContentNames'); - $contentPieces = Report::factory($this->pluginName, 'getContentPieces'); - $reports = array($contentNames, $contentPieces); - - foreach($reports as $report) { - $reportsView->addReport( - $report->getCategory(), - $report->getName(), - 'Contents.' . Report::PREFIX_ACTION_IN_MENU . ucfirst($report->getAction()) - ); - } - - return $reportsView->render(); - } - - public function menuGetContentNames() - { - $report = Report::factory($this->pluginName, 'getContentNames'); - - return View::singleReport($report->getName(), $report->render()); - } - - public function menuGetContentPieces() - { - $report = Report::factory($this->pluginName, 'getContentPieces'); - - return View::singleReport($report->getName(), $report->render()); - } - -} diff --git a/plugins/Contents/Menu.php b/plugins/Contents/Menu.php deleted file mode 100644 index 2e0d25bb7f..0000000000 --- a/plugins/Contents/Menu.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Contents; - -use Piwik\Menu\MenuReporting; - -/** - * This class allows you to add, remove or rename menu items. - * To configure a menu (such as Admin Menu, Reporting Menu, User Menu...) simply call the corresponding methods as - * described in the API-Reference http://developer.piwik.org/api-reference/Piwik/Menu/MenuAbstract - */ -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addActionsItem('Contents_Contents', array('module' => 'Contents', 'action' => 'index'), $orderId = 40); - } -} diff --git a/plugins/Contents/Reports/Base.php b/plugins/Contents/Reports/Base.php index f07baf973d..e323bebdbe 100644 --- a/plugins/Contents/Reports/Base.php +++ b/plugins/Contents/Reports/Base.php @@ -8,18 +8,27 @@ */ namespace Piwik\Plugins\Contents\Reports; -use Piwik\Columns\Dimension; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Contents\Dimensions; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; abstract class Base extends Report { protected function init() { - $this->category = 'General_Actions'; + $this->categoryId = 'General_Actions'; + $this->subcategoryId = 'Contents_Contents'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widget = $factory->createWidget(); + + $widgetsList->addToContainerWidget('Contents', $widget); } /** diff --git a/plugins/Contents/Reports/GetContentNames.php b/plugins/Contents/Reports/GetContentNames.php index d217af5d71..6afb8e071e 100644 --- a/plugins/Contents/Reports/GetContentNames.php +++ b/plugins/Contents/Reports/GetContentNames.php @@ -32,7 +32,6 @@ class GetContentNames extends Base $this->order = 35; $this->actionToLoadSubTables = 'getContentNames'; - $this->widgetTitle = 'Contents_ContentName'; $this->metrics = array('nb_impressions', 'nb_interactions'); $this->processedMetrics = array(new InteractionRate()); } diff --git a/plugins/Contents/Reports/GetContentPieces.php b/plugins/Contents/Reports/GetContentPieces.php index 7ab3f1ed1c..ede8da9405 100644 --- a/plugins/Contents/Reports/GetContentPieces.php +++ b/plugins/Contents/Reports/GetContentPieces.php @@ -32,8 +32,6 @@ class GetContentPieces extends Base $this->order = 36; $this->actionToLoadSubTables = 'getContentPieces'; - $this->widgetTitle = 'Contents_ContentPiece'; - $this->metrics = array('nb_impressions', 'nb_interactions'); $this->processedMetrics = array(new InteractionRate()); } diff --git a/plugins/Contents/Widgets/ContentsByDimension.php b/plugins/Contents/Widgets/ContentsByDimension.php new file mode 100644 index 0000000000..3f2291e8d8 --- /dev/null +++ b/plugins/Contents/Widgets/ContentsByDimension.php @@ -0,0 +1,21 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Contents\Widgets; + +use Piwik\Plugins\CoreHome\CoreHome; +use Piwik\Widget\WidgetContainerConfig; + +class ContentsByDimension extends WidgetContainerConfig +{ + protected $layout = CoreHome::WIDGET_CONTAINER_LAYOUT_BY_DIMENSION; + protected $id = 'Contents'; + protected $categoryId = 'General_Actions'; + protected $subcategoryId = 'Contents_Contents'; + +} diff --git a/plugins/Contents/tests/System/expected/.gitkeep b/plugins/Contents/tests/System/expected/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/plugins/Contents/tests/System/expected/.gitkeep diff --git a/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentNames_lastN__API.getProcessedReport_day.xml b/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentNames_lastN__API.getProcessedReport_day.xml index 5c1cc502a7..8eed477e8b 100644 --- a/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentNames_lastN__API.getProcessedReport_day.xml +++ b/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentNames_lastN__API.getProcessedReport_day.xml @@ -4,6 +4,7 @@ <prettyDate>January 3 – 9, 2010</prettyDate> <metadata> <category>Actions</category> + <subcategory>Contents</subcategory> <name>Content Name</name> <module>Contents</module> <action>getContentNames</action> diff --git a/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml b/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml index b43860be63..f0115ea581 100644 --- a/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml +++ b/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml @@ -4,6 +4,7 @@ <prettyDate>January 3 – 9, 2010</prettyDate> <metadata> <category>Actions</category> + <subcategory>Contents</subcategory> <name>Content Piece</name> <module>Contents</module> <action>getContentPieces</action> diff --git a/plugins/CoreConsole/Commands/GenerateReport.php b/plugins/CoreConsole/Commands/GenerateReport.php index 9eb7313400..3f9fda4939 100644 --- a/plugins/CoreConsole/Commands/GenerateReport.php +++ b/plugins/CoreConsole/Commands/GenerateReport.php @@ -10,8 +10,10 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\Columns\Dimension; +use Piwik\Piwik; use Piwik\Plugin\Manager; use Piwik\Plugin\Report; +use Piwik\Plugin\Reports; use Piwik\Translate; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -67,8 +69,8 @@ class GenerateReport extends GeneratePluginBase $this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles); $this->writeSuccessMessage($output, array( - sprintf('Reports/%s.php for %s generated.', ucfirst($apiName), $pluginName), - 'You should now implement the method called "' . lcfirst($apiName) . '" in API.php', + sprintf('plugins/%s/Reports/%s.php for %s generated.', $pluginName, ucfirst($apiName)), + 'You should now implement the method called <comment>"' . lcfirst($apiName) . '()"</comment> in API.php', // 'Read more about this here: link to developer guide', 'Enjoy!' )); @@ -78,8 +80,10 @@ class GenerateReport extends GeneratePluginBase { $order = 1; - foreach (Report::getAllReports() as $report) { - if ($report->getCategory() === $category) { + $reports = new Reports(); + + foreach ($reports->getAllReports() as $report) { + if ($report->getCategoryId() === $category) { if ($report->getOrder() > $order) { $order = $report->getOrder() + 1; } @@ -189,10 +193,12 @@ class GenerateReport extends GeneratePluginBase $category = $input->getOption('category'); + $reports = new Reports(); + $categories = array(); - foreach (Report::getAllReports() as $report) { - if ($report->getCategory()) { - $categories[] = $report->getCategory(); + foreach ($reports->getAllReports() as $report) { + if ($report->getCategoryId()) { + $categories[] = Piwik::translate($report->getCategoryId()); } } $categories = array_values(array_unique($categories)); @@ -226,7 +232,9 @@ class GenerateReport extends GeneratePluginBase $dimensions = array(); $dimensionNames = array(); - foreach (Report::getAllReports() as $report) { + $reports = new Reports(); + + foreach ($reports->getAllReports() as $report) { $dimension = $report->getDimension(); if (is_object($dimension)) { $name = $dimension->getName(); diff --git a/plugins/CoreConsole/Commands/GenerateWidget.php b/plugins/CoreConsole/Commands/GenerateWidget.php index da4f8e349d..05f92bd4af 100644 --- a/plugins/CoreConsole/Commands/GenerateWidget.php +++ b/plugins/CoreConsole/Commands/GenerateWidget.php @@ -10,8 +10,8 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\Piwik; -use Piwik\Plugin\Widgets; use Piwik\Translate; +use Piwik\Widget\WidgetsList; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -25,6 +25,7 @@ class GenerateWidget extends GeneratePluginBase $this->setName('generate:widget') ->setDescription('Adds a plugin widget class to an existing plugin') ->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have any widgets defined yet') + ->addOption('widgetname', null, InputOption::VALUE_REQUIRED, 'The name of the widget you want to create') ->addOption('category', null, InputOption::VALUE_REQUIRED, 'The name of the category the widget should belong to'); } @@ -33,6 +34,7 @@ class GenerateWidget extends GeneratePluginBase $pluginName = $this->getPluginName($input, $output); $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); + $widgetName = $this->getWidgetName($input, $output); $category = $this->getCategory($input, $output); if ($category === Piwik::translate($category)) { @@ -40,26 +42,78 @@ class GenerateWidget extends GeneratePluginBase $category = $this->makeTranslationIfPossible($pluginName, $category); } + $widgetMethod = $this->getWidgetMethodName($widgetName); + $widgetClass = ucfirst($widgetMethod); + $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; - $replace = array('ExamplePlugin' => $pluginName, - 'Example Category' => $category); - $whitelistFiles = array('/Widgets.php'); + $replace = array('ExamplePlugin' => $pluginName, + 'MyExampleWidget' => $widgetClass, + 'Example Widget Name' => $this->makeTranslationIfPossible($pluginName, $widgetName), + 'Example Widgets' => $category); + $whitelistFiles = array('/Widgets', '/Widgets/MyExampleWidget.php'); $this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles); $this->writeSuccessMessage($output, array( - sprintf('Widgets.php for %s generated.', $pluginName), - 'You can now start defining your plugin widgets', + sprintf('plugins/%s/Widgets/%s.php generated.', $pluginName, $widgetClass), + 'You can now start implementing the <comment>render()</comment> method.', 'Enjoy!' )); } + private function getWidgetMethodName($methodName) + { + $methodName = trim($methodName); + $methodName = str_replace(' ', '', $methodName); + $methodName = preg_replace("/[^A-Za-z0-9]/", '', $methodName); + + if (0 !== strpos(strtolower($methodName), 'get')) { + $methodName = 'get' . ucfirst($methodName); + } + + return lcfirst($methodName); + } + + /** + * @param InputInterface $input + * @param OutputInterface $output + * @return array + * @throws \RuntimeException + */ + protected function getWidgetName(InputInterface $input, OutputInterface $output) + { + $validate = function ($widgetName) { + if (empty($widgetName)) { + throw new \InvalidArgumentException('Please enter the name of your widget'); + } + + if (preg_match("/[^A-Za-z0-9 ]/", $widgetName)) { + throw new \InvalidArgumentException('Only alpha numerical characters and whitespaces are allowed'); + } + + return $widgetName; + }; + + $widgetName = $input->getOption('widgetname'); + + if (empty($widgetName)) { + $dialog = $this->getHelperSet()->get('dialog'); + $widgetName = $dialog->askAndValidate($output, 'Enter the name of your Widget, for instance "Browser Families": ', $validate); + } else { + $validate($widgetName); + } + + $widgetName = ucfirst($widgetName); + + return $widgetName; + } + protected function getExistingCategories() { $categories = array(); - foreach (Widgets::getAllWidgets() as $widget) { - if ($widget->getCategory()) { - $categories[] = Piwik::translate($widget->getCategory()); + foreach (WidgetsList::get()->getWidgetConfigs() as $widget) { + if ($widget->getCategoryId()) { + $categories[] = Piwik::translate($widget->getCategoryId()); } } $categories = array_values(array_unique($categories)); @@ -111,8 +165,8 @@ class GenerateWidget extends GeneratePluginBase */ protected function getPluginName(InputInterface $input, OutputInterface $output) { - $pluginNames = $this->getPluginNamesHavingNotSpecificFile('Widgets.php'); - $invalidName = 'You have to enter the name of an existing plugin which does not already have any widgets defined'; + $pluginNames = $this->getPluginNames(); + $invalidName = 'You have to enter a name of an existing plugin.'; return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName); } diff --git a/plugins/CoreHome/Categories/ActionsCategory.php b/plugins/CoreHome/Categories/ActionsCategory.php new file mode 100644 index 0000000000..e995df110d --- /dev/null +++ b/plugins/CoreHome/Categories/ActionsCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Categories; + +use Piwik\Category\Category; + +class ActionsCategory extends Category +{ + protected $id = 'General_Actions'; + protected $order = 10; +} diff --git a/plugins/CoreHome/Categories/DevicesSubcategory.php b/plugins/CoreHome/Categories/DevicesSubcategory.php new file mode 100644 index 0000000000..9d68e5b30c --- /dev/null +++ b/plugins/CoreHome/Categories/DevicesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Categories; + +use Piwik\Category\Subcategory; + +class DevicesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'DevicesDetection_Devices'; + protected $order = 15; + +} diff --git a/plugins/CoreHome/Categories/EngagementSubcategory.php b/plugins/CoreHome/Categories/EngagementSubcategory.php new file mode 100644 index 0000000000..1a4f4b1d40 --- /dev/null +++ b/plugins/CoreHome/Categories/EngagementSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Categories; + +use Piwik\Category\Subcategory; + +class EngagementSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'VisitorInterest_Engagement'; + protected $order = 30; + +} diff --git a/plugins/CoreHome/Categories/SoftwareSubcategory.php b/plugins/CoreHome/Categories/SoftwareSubcategory.php new file mode 100644 index 0000000000..34188afe97 --- /dev/null +++ b/plugins/CoreHome/Categories/SoftwareSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Categories; + +use Piwik\Category\Subcategory; + +class SoftwareSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'DevicesDetection_Software'; + protected $order = 20; + +} diff --git a/plugins/CoreHome/Categories/VisitorsCategory.php b/plugins/CoreHome/Categories/VisitorsCategory.php new file mode 100644 index 0000000000..c4604c4035 --- /dev/null +++ b/plugins/CoreHome/Categories/VisitorsCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Categories; + +use Piwik\Category\Category; + +class VisitorsCategory extends Category +{ + protected $id = 'General_Visitors'; + protected $order = 5; +} diff --git a/plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php b/plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php new file mode 100644 index 0000000000..3db3e260d2 --- /dev/null +++ b/plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Categories; + +use Piwik\Category\Subcategory; + +class VisitorsOverviewSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'General_Overview'; + protected $order = 2; + +} diff --git a/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php b/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php index e590e19871..b56364aa7f 100644 --- a/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php +++ b/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php @@ -38,7 +38,7 @@ class AverageTimeOnSite extends ProcessedMetric public function format($value, Formatter $formatter) { - return $formatter->getPrettyTimeFromSeconds($value); + return $formatter->getPrettyTimeFromSeconds($value, true); } public function getTranslatedName() diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php index a61419e5fb..a0866c0ff2 100644 --- a/plugins/CoreHome/Controller.php +++ b/plugins/CoreHome/Controller.php @@ -11,12 +11,13 @@ namespace Piwik\Plugins\CoreHome; use Exception; use Piwik\API\Request; use Piwik\Common; +use Piwik\DataTable\Renderer\Json; use Piwik\Date; use Piwik\FrontController; -use Piwik\Menu\MenuReporting; use Piwik\Notification\Manager as NotificationManager; use Piwik\Piwik; use Piwik\Plugin\Report; +use Piwik\Widget\Widget; use Piwik\Plugins\CoreHome\DataTableRowAction\MultiRowEvolution; use Piwik\Plugins\CoreHome\DataTableRowAction\RowEvolution; use Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient; @@ -28,7 +29,6 @@ use Piwik\UpdateCheck; use Piwik\Url; use Piwik\View; use Piwik\ViewDataTable\Manager as ViewDataTableManager; -use Piwik\Plugin\Widgets as PluginWidgets; class Controller extends \Piwik\Plugin\Controller { @@ -49,40 +49,38 @@ class Controller extends \Piwik\Plugin\Controller return 'redirectToCoreHomeIndex'; } - public function renderReportMenu(Report $report) + public function renderReportWidget(Report $report) { Piwik::checkUserHasSomeViewAccess(); $this->checkSitePermission(); $report->checkIsEnabled(); - $menuTitle = $report->getMenuTitle(); - - if (empty($menuTitle)) { - throw new Exception('This report is not supposed to be displayed in the menu, please define a $menuTitle in your report.'); - } - - $menuTitle = $this->translator->translate($menuTitle); - $content = $this->renderReportWidget($report); - - return View::singleReport($menuTitle, $content); + return $report->render(); } - public function renderReportWidget(Report $report) + public function renderWidgetContainer() { Piwik::checkUserHasSomeViewAccess(); $this->checkSitePermission(); - $report->checkIsEnabled(); + $view = new View('@CoreHome/widgetContainer'); + $view->isWidgetized = (bool) Common::getRequestVar('widget', 0, 'int'); + $view->containerId = Common::getRequestVar('containerId', null, 'string'); - return $report->render(); + return $view->render(); } - public function renderWidget(PluginWidgets $widget, $method) + /** + * @param Widget $widget + * @return mixed + * @throws Exception + */ + public function renderWidget($widget) { Piwik::checkUserHasSomeViewAccess(); - return $widget->$method(); + return $widget->render(); } function redirectToCoreHomeIndex() @@ -133,7 +131,6 @@ class Controller extends \Piwik\Plugin\Controller { $view = new View('@CoreHome/getDefaultIndexView'); $this->setGeneralVariablesView($view); - $view->menu = MenuReporting::getInstance()->getMenu(); $view->dashboardSettingsControl = new DashboardManagerControl(); $view->content = ''; return $view; diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index 23da8326aa..c8b2b428d0 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -14,6 +14,13 @@ namespace Piwik\Plugins\CoreHome; class CoreHome extends \Piwik\Plugin { /** + * Defines a widget container layout that will display all widgets within a container inside a "tab" menu + * where on the left side a link is shown for each widget and on the right side the selected widget. + * @api + */ + const WIDGET_CONTAINER_LAYOUT_BY_DIMENSION = 'ByDimension'; + + /** * @see Piwik\Plugin::registerEvents */ public function registerEvents() @@ -105,8 +112,6 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/javascripts/dataTable_rowactions.js"; $jsFiles[] = "plugins/CoreHome/javascripts/popover.js"; $jsFiles[] = "plugins/CoreHome/javascripts/broadcast.js"; - $jsFiles[] = "plugins/CoreHome/javascripts/menu.js"; - $jsFiles[] = "plugins/CoreHome/javascripts/menu_init.js"; $jsFiles[] = "plugins/CoreHome/javascripts/calendar.js"; $jsFiles[] = "plugins/CoreHome/javascripts/sparkline.js"; $jsFiles[] = "plugins/CoreHome/javascripts/corehome.js"; @@ -120,8 +125,12 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.config.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/services/service.module.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/services/global-ajax-queue.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-api.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-url.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/services/report-metadata-model.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/services/reporting-pages-model.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/filter.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/translate.js"; @@ -130,6 +139,7 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/length.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/trim.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/pretty-url.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/escape.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js"; @@ -146,6 +156,8 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/history/history.service.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-model.service.js"; $jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.controller.js"; $jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.directive.js"; @@ -163,6 +175,21 @@ class CoreHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.controller.js"; $jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js"; + + $jsFiles[] = "plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/widget/widget.directive.js"; + + $jsFiles[] = "plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js"; + + $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js"; + + $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js"; } public function getClientSideTranslationKeys(&$translationKeys) @@ -258,5 +285,6 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'CoreHome_UndoPivotBySubtable'; $translationKeys[] = 'CoreHome_PivotBySubtable'; $translationKeys[] = 'General_LearnMore'; + $translationKeys[] = 'CoreHome_NoSuchPage'; } } diff --git a/plugins/CoreHome/Widgets.php b/plugins/CoreHome/Widgets.php deleted file mode 100644 index 17d888bd35..0000000000 --- a/plugins/CoreHome/Widgets.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\CoreHome; - -use Piwik\Common; -use Piwik\Piwik; -use Piwik\Translation\Translator; -use Piwik\View; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'Example Widgets'; - - /** - * @var Translator - */ - private $translator; - - public function __construct(Translator $translator) - { - $this->translator = $translator; - } - - protected function init() - { - $this->addWidget('CoreHome_SupportPiwik', 'getDonateForm'); - $this->addWidget('Installation_Welcome', 'getPromoVideo'); - } - - /** - * Renders and echo's the in-app donate form w/ slider. - */ - public function getDonateForm() - { - $view = new View('@CoreHome/getDonateForm'); - - if (Common::getRequestVar('widget', false) - && Piwik::hasUserSuperUserAccess()) { - $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess'); - } - - return $view->render(); - } - - /** - * Renders and echo's HTML that displays the Piwik promo video. - */ - public function getPromoVideo() - { - $view = new View('@CoreHome/getPromoVideo'); - $view->shareText = $this->translator->translate('CoreHome_SharePiwikShort'); - $view->shareTextLong = $this->translator->translate('CoreHome_SharePiwikLong'); - $view->promoVideoUrl = 'https://www.youtube.com/watch?v=OslfF_EH81g'; - - return $view->render(); - } -} diff --git a/plugins/CoreHome/Widgets/GetDonateForm.php b/plugins/CoreHome/Widgets/GetDonateForm.php new file mode 100644 index 0000000000..093fe8c3a2 --- /dev/null +++ b/plugins/CoreHome/Widgets/GetDonateForm.php @@ -0,0 +1,48 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Widgets; + +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Widget\Widget; +use Piwik\Widget\WidgetConfig; +use Piwik\Translation\Translator; +use Piwik\View; + +class GetDonateForm extends Widget +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Example Widgets'); + $config->setName('CoreHome_SupportPiwik'); + $config->setOrder(5); + } + + public function render() + { + $view = new View('@CoreHome/getDonateForm'); + + if (Common::getRequestVar('widget', false) + && Piwik::hasUserSuperUserAccess()) { + $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess'); + } + + return $view->render(); + } +}
\ No newline at end of file diff --git a/plugins/CoreHome/Widgets/GetPromoVideo.php b/plugins/CoreHome/Widgets/GetPromoVideo.php new file mode 100644 index 0000000000..49636b5e0e --- /dev/null +++ b/plugins/CoreHome/Widgets/GetPromoVideo.php @@ -0,0 +1,44 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreHome\Widgets; + +use Piwik\Widget\Widget; +use Piwik\Widget\WidgetConfig; +use Piwik\Translation\Translator; +use Piwik\View; + +class GetPromoVideo extends Widget +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Example Widgets'); + $config->setName('Installation_Welcome'); + $config->setOrder(10); + } + + public function render() + { + $view = new View('@CoreHome/getPromoVideo'); + $view->shareText = $this->translator->translate('CoreHome_SharePiwikShort'); + $view->shareTextLong = $this->translator->translate('CoreHome_SharePiwikLong'); + $view->promoVideoUrl = 'https://www.youtube.com/watch?v=OslfF_EH81g'; + + return $view->render(); + } +}
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js new file mode 100644 index 0000000000..4417cea7ac --- /dev/null +++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js @@ -0,0 +1,31 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Shows a general loading message while [loading] is set to true. + * + * @param {Boolean} loading If true, the activity indicator is shown, otherwise the indicator is hidden. + * + * Example: + * <div piwik-activity-indicator loading="true|false"></div> + */ +(function () { + angular.module('piwikApp').directive('piwikActivityIndicator', piwikActivityIndicator); + + piwikActivityIndicator.$inject = ['piwik']; + + function piwikActivityIndicator(piwik){ + return { + restrict: 'A', + transclude: true, + scope: { + loading: '=' + }, + templateUrl: 'plugins/CoreHome/angularjs/activity-indicator/activityindicator.html?cb=' + piwik.cacheBuster + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html new file mode 100644 index 0000000000..f6080ae817 --- /dev/null +++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html @@ -0,0 +1,3 @@ +<div ng-show="loading" class="loadingPiwik"> + <img src="plugins/Morpheus/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }} +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js b/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js index 89d572cedf..2eef346ec6 100644 --- a/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js +++ b/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js @@ -132,7 +132,9 @@ scope.ajaxForm.data[name] = val; if (!skipScopeApply) { - scope.$apply(); + setTimeout(function () { + scope.$apply(); + }, 0); } } }; diff --git a/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js b/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js index 0617c5df88..ee1a2cf219 100644 --- a/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js +++ b/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js @@ -24,13 +24,17 @@ function onClickOutsideElement (event) { if (element.has(event.target).length === 0) { - scope.$apply(attr.piwikFocusAnywhereButHere); + setTimeout(function () { + scope.$apply(attr.piwikFocusAnywhereButHere); + }, 0); } } function onEscapeHandler (event) { if (event.which === 27) { - scope.$apply(attr.piwikFocusAnywhereButHere); + setTimeout(function () { + scope.$apply(attr.piwikFocusAnywhereButHere); + }, 0); } } diff --git a/plugins/CoreHome/angularjs/common/filters/escape.js b/plugins/CoreHome/angularjs/common/filters/escape.js new file mode 100644 index 0000000000..382e84b5ac --- /dev/null +++ b/plugins/CoreHome/angularjs/common/filters/escape.js @@ -0,0 +1,16 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.filter').filter('escape', escape); + + function escape() { + + return function(value) { + return piwikHelper.escape(piwikHelper.htmlEntities(value)); + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js b/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js new file mode 100644 index 0000000000..f831995810 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js @@ -0,0 +1,14 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.service').service('globalAjaxQueue', ajaxQueue); + + function ajaxQueue() { + + return globalAjaxQueue; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/common/services/piwik-url.js b/plugins/CoreHome/angularjs/common/services/piwik-url.js new file mode 100644 index 0000000000..f2d6e9bb0c --- /dev/null +++ b/plugins/CoreHome/angularjs/common/services/piwik-url.js @@ -0,0 +1,54 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.service').service('piwikUrl', piwikUrl); + + piwikUrl.$inject = ['$location', 'piwik']; + + /** + * Similar to angulars $location but works around some limitation. Use it if you need to access search params + */ + function piwikUrl($location, piwik) { + + var model = { + getSearchParam: getSearchParam + } + + return model; + + function getSearchParam(paramName) + { + if (paramName === 'segment') { + var hash = window.location.href.split('#'); + if (hash && hash[1]) { + return piwik.broadcast.getValueFromHash(paramName, hash[1]); + } + + return broadcast.getValueFromUrl(paramName); + } + + // available in global scope + var search = $location.search(); + + if (!search[paramName]) { + // see https://github.com/angular/angular.js/issues/7239 (issue is resolved but problem still exists) + search[paramName] = piwik.broadcast.getValueFromUrl(paramName); + } + + if (search[paramName]) { + var value = search[paramName]; + + if (angular.isArray(search[paramName])) { + // use last one. Eg when having period=day&period=year angular would otherwise return ['day', 'year'] + return value[value.length - 1]; + } + + return value; + } + } + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/common/services/report-metadata-model.js b/plugins/CoreHome/angularjs/common/services/report-metadata-model.js new file mode 100644 index 0000000000..f158861423 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/services/report-metadata-model.js @@ -0,0 +1,52 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.service').factory('reportMetadataModel', reportMetadataModel); + + reportMetadataModel.$inject = ['piwik', 'piwikApi']; + + function reportMetadataModel (piwik, piwikApi) { + + var reportsPromise = null; + + var model = { + reports: [], + fetchReportMetadata: fetchReportMetadata, + findReport: findReport + }; + + return model; + + function findReport(module, action) + { + var found = []; + + angular.forEach(model.reports, function (report) { + if (report.module === module && report.action === action) { + found = report; + } + }); + + return found; + } + + function fetchReportMetadata() + { + if (!reportsPromise) { + reportsPromise = piwikApi.fetch({ + method: 'API.getReportMetadata', + idSites: piwik.idSite || piwik.broadcast.getValueFromUrl('idSite'), + }).then(function (response) { + model.reports = response; + return response; + }); + } + + return reportsPromise; + } + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js b/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js new file mode 100644 index 0000000000..1b1e406c95 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js @@ -0,0 +1,58 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.service').factory('reportingPagesModel', reportingPagesModelService); + + reportingPagesModelService.$inject = ['piwikApi']; + + function reportingPagesModelService (piwikApi) { + var fetchAllPagesPromise = false; + + // those sites are going to be displayed + var model = { + pages : [], + findPage: findPage, + reloadAllPages : reloadAllPages, + getAllPages : getAllPages + }; + + return model; + + function findPage(categoryId, subcategoryId) + { + var found = null; + + angular.forEach(model.pages, function (page) { + if (page && + page.category && page.subcategory && + page.category.id === categoryId && ('' + page.subcategory.id) === subcategoryId) { + found = page; + } + }); + + return found; + } + + function reloadAllPages() + { + fetchAllPagesPromise = null; + return getAllPages(); + } + + function getAllPages() + { + if (!fetchAllPagesPromise) { + fetchAllPagesPromise = piwikApi.fetch({method: 'API.getReportPagesMetadata'}).then(function (response) { + model.pages = response; + return response; + }); + } + + return fetchAllPagesPromise; + } + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/history/history.service.js b/plugins/CoreHome/angularjs/history/history.service.js index 2c6486a517..7dadf35ad0 100644 --- a/plugins/CoreHome/angularjs/history/history.service.js +++ b/plugins/CoreHome/angularjs/history/history.service.js @@ -36,6 +36,19 @@ loadCurrentPage(); } + function cleanHash(hash) + { + var chars = ['#', '/', '?']; + for (var i = 0; i != chars.length; ++i) { + var charToRemove = chars[i]; + if (hash.charAt(0) == charToRemove) { + hash = hash.substring(1); + } + } + + return hash; + } + // currently, the AJAX content URL is stored in $location.search(), but before it was stored in $location.path(). // this function makes sure URLs like http://piwik.net/?...#/module=Whatever&action=whatever still work. function changePathToSearch() { @@ -82,13 +95,7 @@ function load(hash) { // make sure the hash is just the query parameter values, w/o a starting #, / or ? char. broadcast.pageload & $location.path should get neither - var chars = ['#', '/', '?']; - for (var i = 0; i != chars.length; ++i) { - var charToRemove = chars[i]; - if (hash.charAt(0) == charToRemove) { - hash = hash.substring(1); - } - } + hash = cleanHash(hash); if (location.hash === '#?' + hash) { loadCurrentPage(); // it would not trigger a location change success event as URL is the same, call it manually diff --git a/plugins/CoreHome/angularjs/http404check.js b/plugins/CoreHome/angularjs/http404check.js index f2e2a86edf..5b720c9472 100644 --- a/plugins/CoreHome/angularjs/http404check.js +++ b/plugins/CoreHome/angularjs/http404check.js @@ -1,9 +1,9 @@ (function () { angular.module('piwikApp').factory('http404CheckInterceptor', http404CheckInterceptor); - http404CheckInterceptor.$inject = ['$q']; + http404CheckInterceptor.$inject = ['$q', 'globalAjaxQueue']; - function http404CheckInterceptor($q) { + function http404CheckInterceptor($q, globalAjaxQueue) { function isClientError(rejection) { @@ -15,8 +15,8 @@ } return { - 'responseError': function(rejection) { + if (rejection && isClientError(rejection) && rejection.config && diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html index d1964d1ac4..14191ac20e 100644 --- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html +++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html @@ -23,7 +23,7 @@ class="reset" src="plugins/CoreHome/images/reset_search.png"/> </div> - <div ng-transclude> + <div ng-transclude ng-click="selectItem($event)"> </div> </div> diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js index 00fbf87d7b..313ef00d6c 100644 --- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js +++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js @@ -34,10 +34,10 @@ templateUrl: 'plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html?cb=' + piwik.cacheBuster, link: function(scope, element, attrs) { - element.find('.item').on('click', function () { - var $self = angular.element(this); + scope.selectItem = function (event) { + var $self = angular.element(event.target); - if ($self.hasClass('disabled') || $self.hasClass('separator')) { + if (!$self.hasClass('item') || $self.hasClass('disabled') || $self.hasClass('separator')) { return; } @@ -47,11 +47,14 @@ }); } scope.$eval('view.showItems = false'); - scope.$apply(); + + setTimeout(function () { + scope.$apply(); + }, 0); element.find('.item').removeClass('active'); $self.addClass('active'); - }); + }; scope.searchItems = function (searchTerm) { diff --git a/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js b/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js new file mode 100644 index 0000000000..ea15f025b0 --- /dev/null +++ b/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js @@ -0,0 +1,74 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * When present in the page it listens to a popover URL parameter. + * + * If present it will try to load the related content in a popover or if the URL is empty it will close an + * opened popover. + * + * Example: + * <div piwik-popover-handler></div> + */ +(function () { + angular.module('piwikApp').directive('piwikPopoverHandler', piwikPopoverHandler); + + piwikPopoverHandler.$inject = ['$location', '$rootScope', 'piwik']; + + function piwikPopoverHandler($location, $rootScope, piwik){ + + return { + restrict: 'A', + scope: {}, + controller: function () { + + function close() + { + Piwik_Popover.close(); + } + + function open(popoverParam) + { + // in case the $ was encoded (e.g. when using copy&paste on urls in some browsers) + popoverParam = decodeURIComponent(popoverParam); + // revert special encoding from broadcast.propagateNewPopoverParameter() + popoverParam = popoverParam.replace(/\$/g, '%'); + popoverParam = decodeURIComponent(popoverParam); + + var popoverParamParts = popoverParam.split(':'); + var handlerName = popoverParamParts[0]; + popoverParamParts.shift(); + var param = popoverParamParts.join(':'); + if (typeof piwik.broadcast.popoverHandlers[handlerName] != 'undefined' + && !piwik.broadcast.isLoginPage()) { + piwik.broadcast.popoverHandlers[handlerName](param); + } + } + + function openOrClose() + { + // should be rather done by routing + var popoverParam = $location.search().popover; + if (popoverParam) { + open(popoverParam); + } else { + close(); + } + } + + $rootScope.$on('$locationChangeSuccess', function () { + // should be rather done by routing + $(function () { + // make sure all popover handles were registered + openOrClose(); + }); + }); + + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js new file mode 100644 index 0000000000..adc1b97936 --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js @@ -0,0 +1,153 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').factory('reportingMenuModel', reportingMenuModelService); + + reportingMenuModelService.$inject = ['$filter', '$q', 'piwikApi', 'reportingPagesModel', '$location']; + + function reportingMenuModelService ($filter, $q, piwikApi, reportingPagesModel, $location) { + + // those sites are going to be displayed + var model = { + menu: [], + selected: [], + fetchMenuItems: fetchMenuItems, + reloadMenuItems: reloadMenuItems, + findSubcategory: findSubcategory + }; + + return model; + + function isNumeric(text) { + return !isNaN(parseFloat(text)) && isFinite(text); + } + + function findSubcategory(categoryId, subcategoryId) + { + var foundCategory = null; + var foundSubcategory = null; + + angular.forEach(model.menu, function (category) { + if (category.id !== categoryId) { + return; + } + angular.forEach(category.subcategories, function (subcategory) { + if (subcategory.id === subcategoryId) { + foundCategory = category; + foundSubcategory = subcategory; + } + }); + }); + + return {category: foundCategory, subcategory: foundSubcategory}; + } + + function buildMenuFromPages(pages) + { + var menu = []; + + var activeCategory = $location.search().category; + var activeSubcategory = $location.search().subcategory; + + var categoriesHandled = {}; + angular.forEach(pages, function (page, key) { + var category = page.category; + var categoryId = category.id; + + if (categoriesHandled[categoryId]) { + return; + } + + categoriesHandled[categoryId] = true; + + if (activeCategory && category.id === activeCategory) { + // this doesn't really belong here but placed it here for convenience + category.active = true; + category.hover = true; + } + + category.subcategories = []; + + var goalsGroup = false; + + angular.forEach(pages, function (page, key) { + if (page.category.id === categoryId) { + var subcategory = page.subcategory; + + if (subcategory.id === activeSubcategory) { + subcategory.active = true; + } + + if (page.widgets && page.widgets[0] && page.category.id === 'Goals_Goals' && isNumeric(page.subcategory.id)) { + // we handle a goal + if (!goalsGroup) { + goalsGroup = angular.copy(subcategory); + goalsGroup.name = $filter('translate')('Goals_ChooseGoal'); + goalsGroup.isGroup = true; + goalsGroup.subcategories = []; + goalsGroup.order = 10; + } + + if (subcategory.active) { + goalsGroup.name = subcategory.name; + } + + var goalId = page.subcategory.id; + subcategory.tooltip = subcategory.name + ' (id = ' + goalId + ' )'; + + goalsGroup.subcategories.push(subcategory); + return; + } + + category.subcategories.push(subcategory); + } + }); + + if (goalsGroup && goalsGroup.subcategories && goalsGroup.subcategories.length <= 3) { + angular.forEach(goalsGroup.subcategories, function (subcategory) { + category.subcategories.push(subcategory); + }); + } else if(goalsGroup) { + category.subcategories.push(goalsGroup); + } + + category.subcategories = sortMenuItems(category.subcategories); + + menu.push(category); + + return menu; + }); + + menu = sortMenuItems(menu); + + return menu; + } + + function sortMenuItems(menu) { + return $filter('orderBy')(menu, 'order'); + }; + + function reloadMenuItems() + { + var pagesPromise = reportingPagesModel.reloadAllPages(); + return pagesPromise.then(function (pages) { + model.menu = buildMenuFromPages(pages); + }); + } + + function fetchMenuItems() + { + var pagesPromise = reportingPagesModel.getAllPages(); + + return pagesPromise.then(function (pages) { + model.menu = buildMenuFromPages(pages); + + return model.menu; + }); + } + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js new file mode 100644 index 0000000000..85dc0f17c5 --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js @@ -0,0 +1,125 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').controller('ReportingMenuController', ReportingMenuController); + + ReportingMenuController.$inject = ['$scope', 'piwik', '$location', '$timeout', 'reportingMenuModel', '$rootScope']; + + function ReportingMenuController($scope, piwik, $location, $timeout, menuModel, $rootScope) { + + function markAllCategoriesAsInactive() + { + angular.forEach(menuModel.menu, function (cat) { + cat.active = false; + cat.hover = false; + angular.forEach(cat.subcategories, function (subcat) { + subcat.active = false; + }); + }); + } + + function getUrlParam(param) + { + var value = piwik.broadcast.getValueFromHash(param); + if (!value) { + value = piwik.broadcast.getValueFromUrl(param); + } + return value; + } + + $scope.menuModel = menuModel; + + var timeoutPromise = null; + + // show subcategories of the currently hovered category + $scope.enterCategory = function (category) { + + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + + angular.forEach(menuModel.menu, function (cat) { + cat.hover = false; + }); + + category.hover = true; + }; + + // show subcategories of the current active category again (after 2 sec max) + $scope.leaveCategory = function (category) { + + if (timeoutPromise) { + $timeout.cancel(timeoutPromise); + } + + timeoutPromise = $timeout(function () { + angular.forEach(menuModel.menu, function (cat) { + if (cat.active) { + cat.hover = true; + } else { + cat.hover = false; + } + }); + }, 2000); + }; + + // highlight the currently hovered subcategory (and category) + $scope.enterSubcategory = function (category, subcategory) { + if (!category || !subcategory) { + return; + } + + markAllCategoriesAsInactive(); + + category.active = true; + category.hover = true; + subcategory.active = true; + }; + + var idSite = getUrlParam('idSite'); + var period = getUrlParam('period'); + var date = getUrlParam('date'); + var segment = getUrlParam('segment'); + + $scope.makeUrl = function (category, subcategory) { + var url = 'idSite=' + idSite + '&period=' + period + '&date=' + date + '&category=' + category.id + '&subcategory=' + subcategory.id; + if (segment) { + url+= '&segment='+ segment; + } + return url; + } + + $scope.loadSubcategory = function (category, subcategory) { + if (subcategory && subcategory.active) { + // this menu item is already active, a location change success would not be triggered, + // instead trigger an event + $rootScope.$emit('loadPage', category.id, subcategory.id); + } + }; + + menuModel.fetchMenuItems().then(function (menu) { + if (!$location.search().subcategory) { + // load first, initial page if no subcategory is present + $scope.enterSubcategory(menu[0], menu[0].subcategories[0]); + $location.search($scope.makeUrl(menu[0], menu[0].subcategories[0])); + } + }); + + $rootScope.$on('$locationChangeSuccess', function () { + var category = $location.search().category; + var subcategory = $location.search().subcategory; + + if (!category || !subcategory) { + return; + } + + var found = menuModel.findSubcategory(category, subcategory); + $scope.enterSubcategory(found.category, found.subcategory); + }); + + } +})(); diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html new file mode 100644 index 0000000000..bbfd687517 --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html @@ -0,0 +1,40 @@ +<div class="Menu--dashboard"> + <ul class="Menu-tabList"> + <li ng-repeat="category in menuModel.menu" + ng-class="{'sfActive': category.active, 'sfHover': category.hover}" + ng-mouseenter="enterCategory(category)" + ng-mouseleave="leaveCategory(category)"> + <a class="menuItem" + ng-href="#?{{ makeUrl(category,category.subcategories[0]) }}" + ng-click="loadSubcategory(category, category.subcategories[0])"> + {{ category.name }} + </a> + <ul ng-show="(category.subcategories|length) > 1"> + <li ng-repeat="subcategory in category.subcategories" + ng-class="{'sfHover': subcategory.active}"> + <div ng-if="subcategory.isGroup" piwik-menudropdown show-search="true" menu-title="{{ subcategory.name|escape }}"> + <a class="item" + ng-repeat="subcat in subcategory.subcategories" + title="{{ subcat.tooltip }}" + ng-class="{'active': subcat.active}" + ng-href="#?{{ makeUrl(category,subcat) }}" + ng-click='loadSubcategory(category, subcat)'> + {{ subcat.name }} + </a> + </div> + + <a ng-if="!subcategory.isGroup" + ng-href="#?{{ makeUrl(category,subcategory) }}" + class="menuItem" + ng-click='loadSubcategory(category, subcategory)'> + {{ subcategory.name }} + </a> + </li> + </ul> + </li> + <li id="Searchmenu"> + <span piwik-quick-access></span> + </li> + </ul> + +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js new file mode 100644 index 0000000000..7cdd9e951b --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js @@ -0,0 +1,31 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Shows the Piwik reporting menu. + * + * It automatically calls the API to fetch all data. + * + * Example: + * <div piwik-reporting-menu></div> + */ + +(function () { + angular.module('piwikApp').directive('piwikReportingMenu', piwikReportingMenu); + + piwikReportingMenu.$inject = ['piwik']; + + function piwikReportingMenu(piwik){ + + return { + restrict: 'A', + scope: {}, + templateUrl: 'plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html?cb=' + piwik.cacheBuster, + controller: 'ReportingMenuController' + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js new file mode 100644 index 0000000000..4018fcee8c --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js @@ -0,0 +1,196 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').factory('reportingPageModel', reportingPageModelService); + + reportingPageModelService.$inject = ['$filter', 'piwikApi', 'reportingPagesModel', 'reportMetadataModel']; + + function reportingPageModelService ($filter, piwikApi, reportingPagesModel, reportMetadataModel) { + var init = false; + + // those sites are going to be displayed + var model = { + fetchPage: fetchPage, + resetPage: resetPage, + widgets: [], + page: null, + pageContentUrl: '', + evolutionReports: [], + sparklineReports: [] + }; + + return model; + + function resetPage() + { + model.page = null; + model.widgets = []; + model.pageContentUrl = ''; + model.evolutionReports = []; + model.sparklineReports = []; + } + + function sortWidgets(widgets) + { + return $filter('orderBy')(widgets, 'order'); + } + + function shouldBeRenderedWithFullWidth(widget) + { + // rather controller logic + if ((widget.isContainer && widget.layout && widget.layout === 'ByDimension') + || widget.viewDataTable === 'bydimension') { + return true; + } + + return widget.viewDataTable && widget.viewDataTable === 'tableAllColumns'; + } + + function buildPage(page) + { + if (!page) { + return; + } + + var widgets = []; + var evolutionReports = []; + var sparklineReports = []; + var reportsToIgnore = []; + + angular.forEach(page.widgets, function (widget) { + + if (isIgnoredReport(reportsToIgnore, widget)) { + return; + } + + reportsToIgnore = reportsToIgnore.concat(getRelatedReports(widget)); + + if (widget.viewDataTable && widget.viewDataTable === 'graphEvolution') { + evolutionReports.push(widget); + } else if (widget.viewDataTable && widget.viewDataTable === 'sparklines') { + sparklineReports.push(widget); + } else { + widgets.push(widget); + } + }); + + widgets = sortWidgets(widgets); + + var groupedWidgets = []; + + if (widgets.length === 1) { + // if there is only one widget, we always display it full width + groupedWidgets = widgets; + } else { + for (var i = 0; i < widgets.length; i++) { + var widget = widgets[i]; + + if (shouldBeRenderedWithFullWidth(widget) || (widgets[i+1] && shouldBeRenderedWithFullWidth(widgets[i+1]))) { + widget.widgets = sortWidgets(widget.widgets); + + groupedWidgets.push(widget); + } else { + + var counter = 0; + var left = [widget]; + var right = []; + + while (widgets[i+1] && !shouldBeRenderedWithFullWidth(widgets[i+1])) { + i++; + counter++; + if (counter % 2 === 0) { + left.push(widgets[i]); + } else { + right.push(widgets[i]); + } + } + + groupedWidgets.push({group: true, left: left, right: right}); + } + } + } + + var copyWidgets = angular.copy(groupedWidgets); + var copyEvolution = angular.copy(evolutionReports); + var copySparklines = angular.copy(sparklineReports); + + if (copyEvolution.length) { + copyEvolution = markWidgetsInFirstRowOfPage(copyEvolution); + } else if (copySparklines.length) { + copySparklines = markWidgetsInFirstRowOfPage(copySparklines); + } else { + copyWidgets = markWidgetsInFirstRowOfPage(copyWidgets); + } + + // angular.copy forces the page to re-render. Otherwise it won't reload some pages + model.evolutionReports = copyEvolution; + model.sparklineReports = copySparklines; + model.widgets = copyWidgets; + } + + function markWidgetsInFirstRowOfPage(widgets) + { + if (widgets && widgets[0]) { + if (widgets[0].group) { + markWidgetsInFirstRowOfPage(widgets[0].left); + markWidgetsInFirstRowOfPage(widgets[0].right); + } else { + widgets[0].isFirstInPage = true; + } + } + + return widgets; + } + + function getRelatedReports(widget) + { + if (widget.isReport) { + var report = reportMetadataModel.findReport(widget.module, widget.action); + + if (report && report.relatedReports) { + return report.relatedReports; + } + } + + return []; + } + + function isIgnoredReport(reportsToIgnore, widget) + { + var found = false; + + if (widget.isReport) { + angular.forEach(reportsToIgnore, function (report) { + if (report.module === widget.module && + report.action === widget.action) { + found = true; + } + }); + } + + return found; + } + + function fetchPage(category, subcategory) + { + resetPage(); + + var pagesPromise = reportingPagesModel.getAllPages(); + var reportsPromise = reportMetadataModel.fetchReportMetadata(); + + return pagesPromise.then(function () { + model.page = reportingPagesModel.findPage(category, subcategory); + + reportsPromise.then(function () { + buildPage(model.page); + }); + + return model.page; + }); + } + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js new file mode 100644 index 0000000000..cf0d424b55 --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js @@ -0,0 +1,56 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp').controller('ReportingPageController', ReportingPageController); + + ReportingPageController.$inject = ['$scope', 'piwik', '$rootScope', '$location', 'reportingPageModel']; + + function ReportingPageController($scope, piwik, $rootScope, $location, pageModel) { + pageModel.resetPage(); + $scope.pageModel = pageModel; + + var currentCategory = null; + var currentSubcategory = null; + + $scope.renderPage = function (category, subcategory) { + if (!category || !subcategory) { + pageModel.resetPage(); + $scope.loading = false; + return; + } + + currentCategory = category; + currentSubcategory = subcategory; + + pageModel.fetchPage(category, subcategory).then(function () { + $scope.hasNoPage = !pageModel.page; + $scope.loading = false; + }); + } + + $scope.loading = true; // we only set loading on initial load + + $scope.renderPage($location.search().category, $location.search().subcategory); + + $rootScope.$on('$locationChangeSuccess', function () { + // should be handled by $route + var category = $location.search().category; + var subcategory = $location.search().subcategory; + + if (category === currentCategory && subcategory === currentSubcategory) { + // this page is already loaded + return; + } + + $scope.renderPage(category, subcategory); + }); + + $rootScope.$on('loadPage', function (event, category, subcategory) { + $scope.renderPage(category, subcategory); + }); + } +})(); diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html new file mode 100644 index 0000000000..9a8974e313 --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html @@ -0,0 +1,25 @@ +<div class="reporting-page"> + + <div piwik-activity-indicator loading="loading"/> + + <div ng-show="hasNoPage">{{ 'CoreHome_NoSuchPage'|translate }}</div> + + <div class="row" ng-repeat="evolutionReport in pageModel.evolutionReports"> + <div class="col-md-12" piwik-widget="evolutionReport"></div> + </div> + + <div class="row" ng-repeat="sparklineReport in pageModel.sparklineReports"> + <div class="col-md-12" piwik-widget="sparklineReport"></div> + </div> + + <div class="row" ng-repeat="widget in pageModel.widgets"> + <div class="col-md-12" ng-if="!widget.group" piwik-widget="widget"></div> + + <div ng-if="widget.group" class="col-md-6"> + <div ng-repeat="widgetInGroup in widget.left" piwik-widget="widgetInGroup"></div> + </div> + <div ng-if="widget.group" class="col-md-6"> + <div ng-repeat="widgetInGroup in widget.right" piwik-widget="widgetInGroup"></div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js new file mode 100644 index 0000000000..a146ed3112 --- /dev/null +++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js @@ -0,0 +1,31 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Shows a piwik reporting page. + * + * The content to be displayed is automatically loaded via API based on the current URL. The URL parameters + * 'category' and 'subcategory' need to be present in the URL in order to see something in the reporting page. + * + * Example: + * <div piwik-reporting-page></div> + */ +(function () { + angular.module('piwikApp').directive('piwikReportingPage', piwikReportingPage); + + piwikReportingPage.$inject = ['piwik']; + + function piwikReportingPage(piwik){ + + return { + restrict: 'A', + scope: {}, + templateUrl: 'plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html?cb=' + piwik.cacheBuster, + controller: 'ReportingPageController' + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html new file mode 100644 index 0000000000..db98dee895 --- /dev/null +++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html @@ -0,0 +1,25 @@ +<div class="reportsByDimensionView"> + + <div class="entityList"> + <div class='dimensionCategory' ng-repeat="category in widgetsByCategory"> + {{ category.name }} + <ul class='listCircle'> + <li ng-repeat="widget in category.widgets" + class="reportDimension" + ng-class="{activeDimension: (selectedWidget.uniqueId===widget.uniqueId)}" + ng-click="selectWidget(widget)"> + <span class='dimension'>{{widget.name}}</span> + </li> + </ul> + </div> + </div> + + <div style="float:left;max-width:900px;"> + <h2 ng-if="selectedWidget.name" class="noTopMargin">{{ selectedWidget.name }}</h2> + + <div ng-if="selectedWidget.parameters" class="dimensionReport" + piwik-widget-loader="selectedWidget.parameters"></div> + </div> + <div class="clear"></div> + +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js new file mode 100644 index 0000000000..1620be2a9d --- /dev/null +++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js @@ -0,0 +1,67 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Renders a widget that is a container widget having the layout "ByDimension". + * + * The "ByDimension" layout shows a menu on the left letting you choose any widgets within this container. The + * currently selected widget is shown on the right. + * + * @param {Object} piwikWidgetByDimensionContainer a widget object as returned by the WidgetMetadata API. + * + * Example: + * <div piwik-widget-by-dimension-container="containerWidget"></div> + */ +(function () { + angular.module('piwikApp').directive('piwikWidgetByDimensionContainer', piwikWidgetContainer); + + piwikWidgetContainer.$inject = ['piwik', '$filter']; + + function piwikWidgetContainer(piwik, $filter){ + return { + restrict: 'A', + scope: { + container: '=piwikWidgetByDimensionContainer' + }, + templateUrl: 'plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html?cb=' + piwik.cacheBuster, + compile: function (element, attrs) { + + return function (scope, element, attrs, ngModel) { + + var widgetsSorted = $filter('orderBy')(scope.container.widgets, 'order'); + var widgetsByCategory = {}; + + angular.forEach(widgetsSorted, function (widget) { + var category = widget.subcategory.name; + + if (!widgetsByCategory[category]) { + widgetsByCategory[category] = {name: category, order: widget.order, widgets: []}; + } + + widgetsByCategory[category].widgets.push(widget); + }); + + // only an array can be sorted + var finalWidgetsByCategory = []; + angular.forEach(widgetsByCategory, function (category) { + finalWidgetsByCategory.push(category); + }); + + scope.widgetsByCategory = $filter('orderBy')(finalWidgetsByCategory, 'order'); + + scope.selectWidget = function (widget) { + scope.selectedWidget = widget; + } + + if (widgetsSorted && widgetsSorted.length) { + scope.selectWidget(widgetsSorted[0]); + } + }; + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html new file mode 100644 index 0000000000..21ebd3d0cd --- /dev/null +++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html @@ -0,0 +1,10 @@ +<div> + <!-- custom template prevents recursion --> + <script id="mywidget.html" type="text/ng-template"> + <div piwik-widget="widget"></div> + </script> + + <div ng-repeat="widget in container.widgets"> + <div ng-include src="'mywidget.html'"/> + </div> +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js new file mode 100644 index 0000000000..c378637c71 --- /dev/null +++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js @@ -0,0 +1,32 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Renders a widget that is a container widget having no specific layout (which is the default). + * + * It shows all widgets vertically aligned one widget after another. + * + * @param {Object} piwikWidgetContainer a widget object as returned by the WidgetMetadata API. + * + * Example: + * <div piwik-widget-container="containerWidget"></div> + */ +(function () { + angular.module('piwikApp').directive('piwikWidgetContainer', piwikWidgetContainer); + + piwikWidgetContainer.$inject = ['piwik']; + + function piwikWidgetContainer(piwik){ + return { + restrict: 'A', + scope: { + container: '=piwikWidgetContainer' + }, + templateUrl: 'plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html?cb=' + piwik.cacheBuster + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html new file mode 100644 index 0000000000..d1f0cb51f1 --- /dev/null +++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html @@ -0,0 +1,13 @@ +<div> + + <div piwik-activity-indicator loading="loading"/> + + <div ng-show="loadingFailed"> + <div class="notification system notification-error"> + {{ 'General_ErrorRequest'|translate:(''):('') }} + </div> + </div> + + <div class="theWidgetContent"></div> + +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js new file mode 100644 index 0000000000..4e1859e789 --- /dev/null +++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js @@ -0,0 +1,134 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Loads any custom widget or URL based on the given parameters. + * + * The currently active idSite, period, date and segment (if needed) is automatically appended to the parameters. If + * this widget is removed from the DOM and requests are in progress, these requests will be aborted. A loading message + * or an error message on failure is shown as well. It's kinda similar to ng-include but there it is not possible to + * listen to HTTP errors etc. + * + * Example: + * <div piwik-widget-loader="{module: '', action: '', ...}"></div> + */ +(function () { + angular.module('piwikApp').directive('piwikWidgetLoader', piwikWidgetLoader); + + piwikWidgetLoader.$inject = ['piwik', 'piwikUrl', '$http', '$compile', '$q']; + + function piwikWidgetLoader(piwik, piwikUrl, $http, $compile, $q){ + return { + restrict: 'A', + transclude: true, + scope: { + piwikWidgetLoader: '=' + }, + templateUrl: 'plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html?cb=' + piwik.cacheBuster, + compile: function (element, attrs) { + + return function (scope, element, attrs, ngModel) { + var changeCounter = 0, + currentScope, + currentElement, + httpCanceler, + contentNode = element.find('.theWidgetContent'); + + var cleanupLastWidgetContent = function() { + if (currentElement) { + currentElement.remove(); + currentElement = null; + } + if (currentScope) { + currentScope.$destroy(); + currentScope = null; + } + }; + + var abortHttpRequestIfNeeded = function () { + if (httpCanceler) { + httpCanceler.resolve(); + httpCanceler = null; + } + } + + function getFullWidgetUrl(parameters) { + + var url = $.param(parameters); + + var idSite = piwikUrl.getSearchParam('idSite'); + var period = piwikUrl.getSearchParam('period'); + var date = piwikUrl.getSearchParam('date'); + var segment = piwikUrl.getSearchParam('segment'); + + url += '&idSite=' + idSite + '&period=' + period; + url += '&date=' + date + '&random=' + parseInt(Math.random() * 10000); + + if (segment) { + url += '&segment=' + segment; + } + + return '?' + url; + } + + function loadWidgetUrl(parameters, thisChangeId) + { + scope.loading = true; + + var url = getFullWidgetUrl(parameters); + + abortHttpRequestIfNeeded(); + cleanupLastWidgetContent(); + + httpCanceler = $q.defer(); + + $http.get(url, {timeout: httpCanceler.promise}).success(function(response) { + if (thisChangeId !== changeCounter || !response) { + // another widget was requested meanwhile, ignore this response + return; + } + + httpCanceler = null; + + var newScope = scope.$new(); + currentScope = newScope; + + scope.loading = false; + scope.loadingFailed = false; + + currentElement = contentNode.html(response).children(); + $compile(currentElement)(newScope); + + }).error(function () { + if (thisChangeId !== changeCounter) { + // another widget was requested meanwhile, ignore this response + return; + } + + httpCanceler = null; + + cleanupLastWidgetContent(); + + scope.loading = false; + scope.loadingFailed = true; + }); + } + + scope.$watch('piwikWidgetLoader', function (parameters, oldUrl) { + if (parameters) { + loadWidgetUrl(parameters, ++changeCounter); + } + }); + + element.on('$destroy', function() { + abortHttpRequestIfNeeded(); + }); + }; + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.html b/plugins/CoreHome/angularjs/widget/widget.directive.html new file mode 100644 index 0000000000..86cdc91291 --- /dev/null +++ b/plugins/CoreHome/angularjs/widget/widget.directive.html @@ -0,0 +1,23 @@ +<div id="{{ widget.uniqueId }}" + ng-show="view.showWidget" + class="{{widget.viewDataTable}}" + ng-class="{'smallTopMargin': (!widget.isFirstInPage && (!widget.name || widgetized))}" + > + <!--smallTopMargin: we display small margin if it's not the first widget on the page and if there's no headline --> + <h2 ng-if="!widget.parameters.widget && widget.name && !widgetized" + piwik-enriched-headline + ng-class="{'noTopMargin': widget.isFirstInPage}" + feature-name="{{widget.name}}">{{widget.name}}</h2> + <h2 ng-if="widget.parameters.widget && widget.name && !widgetized">{{widget.name}}</h2> + + <div ng-if="!widget.isContainer && widget.parameters" + piwik-widget-loader="widget.parameters"></div> + + <div ng-if="widget.isContainer && widget.layout!='ByDimension'"> + <div piwik-widget-container="widget"></div> + </div> + + <div ng-if="widget.isContainer && widget.layout=='ByDimension'"> + <div piwik-widget-by-dimension-container="widget"></div> + </div> +</div>
\ No newline at end of file diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.js b/plugins/CoreHome/angularjs/widget/widget.directive.js new file mode 100644 index 0000000000..86574870f2 --- /dev/null +++ b/plugins/CoreHome/angularjs/widget/widget.directive.js @@ -0,0 +1,93 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Renders any kind of widget. If you have a widget and you want to have it rendered, use this directive. It will + * display a name on top and the actual widget below. It can handle any kind of widget, no matter whether it is a + * regular widget or a container. + * + * @param {Object} piwikWidget A widget object as returned by the WidgetMetadata API. + * @param {Object} piwikWidget.middlewareParameters If present, we will request a URL using the given parameters and + * only if this URL returns a JSON `true` the widget will be shown. + * Otherwise the widget won't be shown. + * @param {String} containerId If you do not have a widget object but a containerId we will find the correct widget + * object based on the given containerId. Be aware that we might not find the widget if + * it is for example not available for the current user or period/date. + * @param {Boolean} widgetized true if the widget is widgetized (eg in Dashboard or exported). In this case we will add + * a URL parameter widget=1 to all widgets. Eg sparklines will be then displayed one after + * another (vertically aligned) instead of two next to each other. + * + * Example: + * <div piwik-widget="widget"></div> + * <div piwik-widget containerid="widgetGoalsOverview"></div> // in this case we will find the correct widget automatically + * <div piwik-widget="widget" widetized="true"></div> // disables rating feature, no initial headline + */ +(function () { + angular.module('piwikApp').directive('piwikWidget', piwikWidget); + + piwikWidget.$inject = ['piwik', 'piwikApi']; + + function piwikWidget(piwik, piwikApi){ + + function findContainerWidget(containerId, scope) { + widgetsHelper.getAvailableWidgets(function (categorizedWidgets) { + + angular.forEach(categorizedWidgets, function (widgets) { + angular.forEach(widgets, function (widget) { + + if (widget && widget.isContainer && widget.parameters.containerId === containerId) { + widget = angular.copy(widget); + if (scope.widgetized) { + widget.isFirstInPage = '1'; + widget.parameters.widget = '1'; + angular.forEach(widget.widgets, function (widget) { + widget.parameters.widget = '1'; + }); + } + scope.widget = widget; + applyMiddleware(scope); + } + }); + }); + + }); + } + + function applyMiddleware(scope) + { + if (!scope.widget.middlewareParameters) { + scope.$eval('view.showWidget = true'); + } else { + var params = angular.copy(scope.widget.middlewareParameters); + piwikApi.fetch(params).then(function (response) { + var enabled = response ? 'true' : 'false'; + scope.$eval('view.showWidget = ' + enabled); + }); + } + } + + return { + restrict: 'A', + scope: { + widget: '=?piwikWidget', + widgetized: '=?', + containerid: '=' + }, + templateUrl: 'plugins/CoreHome/angularjs/widget/widget.directive.html?cb=' + piwik.cacheBuster, + compile: function (element, attrs) { + + return function (scope, element, attrs, ngModel) { + if (scope.widget) { + applyMiddleware(scope); + } else if (attrs.containerid) { + findContainerWidget(attrs.containerid, scope); + } + } + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js index 706a8e59d5..3d5fb0432c 100644 --- a/plugins/CoreHome/javascripts/broadcast.js +++ b/plugins/CoreHome/javascripts/broadcast.js @@ -159,7 +159,6 @@ var broadcast = { } else { // start page Piwik_Popover.close(); - $('.pageWrap #content:not(.admin)').empty(); } }, @@ -173,6 +172,7 @@ var broadcast = { }, /** + * ONLY USED BY OVERLAY * propagateAjax -- update hash values then make ajax calls. * example : * 1) <a href="javascript:broadcast.propagateAjax('module=Referrers&action=getKeywords')">View keywords report</a> @@ -229,6 +229,47 @@ var broadcast = { }, /** + * propagateAjax -- update hash values then make ajax calls. + * example : + * 1) <a href="javascript:broadcast.propagateAjax('module=Referrers&action=getKeywords')">View keywords report</a> + * 2) Main menu li also goes through this function. + * + * Will propagate your new value into the current hash string and make ajax calls. + * + * NOTE: this method will only make ajax call and replacing main content. + * + * @param {string} ajaxUrl querystring with parameters to be updated + * @param {boolean} [disableHistory] the hash change won't be available in the browser history + * @return {void} + */ + buildReportingUrl: function (ajaxUrl, disableHistory) { + + // available in global scope + var currentHashStr = broadcast.getHash(); + + ajaxUrl = ajaxUrl.replace(/^\?|&#/, ''); + + var params_vals = ajaxUrl.split("&"); + for (var i = 0; i < params_vals.length; i++) { + currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr); + } + + // if the module is not 'Goals', we specifically unset the 'idGoal' parameter + // this is to ensure that the URLs are clean (and that clicks on graphs work as expected - they are broken with the extra parameter) + var action = broadcast.getParamValue('action', currentHashStr); + if (action != 'goalReport' && action != 'ecommerceReport' && action != 'products' && action != 'sales') { + currentHashStr = broadcast.updateParamValue('idGoal=', currentHashStr); + } + // unset idDashboard if use doesn't display a dashboard + var module = broadcast.getParamValue('module', currentHashStr); + if (module != 'Dashboard') { + currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr); + } + + return '#' + currentHashStr; + }, + + /** * propagateNewPage() -- update url value and load new page, * Example: * 1) We want to update idSite to both search query and hash then reload the page, @@ -349,9 +390,9 @@ var broadcast = { */ propagateNewPopoverParameter: function (handlerName, value) { // init broadcast if not already done (it is required to make popovers work in widgetize mode) - broadcast.init(true); + //broadcast.init(true); - var hash = broadcast.getHashFromUrl(window.location.href); + var $location = angular.element(document).injector().get('$location'); var popover = ''; if (handlerName) { @@ -365,24 +406,14 @@ var broadcast = { } if ('' == value || 'undefined' == typeof value) { - var newHash = hash.replace(/(&?popover=.*)/, ''); - } else if (broadcast.getParamValue('popover', hash)) { - var newHash = broadcast.updateParamValue('popover='+popover, hash); - } else if (hash && hash != '#') { - var newHash = hash + '&popover=' + popover + $location.search('popover', ''); } else { - var newHash = '#popover='+popover; - } - - // never use an empty hash, as that might reload the page - if ('' == newHash) { - newHash = '#'; + $location.search('popover', popover); } - broadcast.forceReload = false; - angular.element(document).injector().invoke(function (historyService) { - historyService.load(newHash); - }); + setTimeout(function () { + angular.element(document).injector().get('$rootScope').$apply(); + }, 1); }, /** diff --git a/plugins/CoreHome/javascripts/corehome.js b/plugins/CoreHome/javascripts/corehome.js index 5bddc23f9e..6e8729611b 100755 --- a/plugins/CoreHome/javascripts/corehome.js +++ b/plugins/CoreHome/javascripts/corehome.js @@ -114,60 +114,6 @@ handleSectionToggle(this, 'inline', !$(this).is(':checked')); }); - // - // reports by dimension list behavior - // - - // when a report dimension is clicked, load the appropriate report - var currentWidgetLoading = null; - $('body').on('click', '.reportDimension', function (e) { - var view = $(this).closest('.reportsByDimensionView'), - report = $('.dimensionReport', view), - loading = $('.loadingPiwik', view); - - // make this dimension the active one - $('.activeDimension', view).removeClass('activeDimension'); - $(this).addClass('activeDimension'); - - // hide the visible report & show the loading elem - report.hide(); - loading.show(); - - // load the report using the data-url attribute (which holds the URL to the report) - var widgetParams = broadcast.getValuesFromUrl($(this).attr('data-url')); - for (var key in widgetParams) { - widgetParams[key] = decodeURIComponent(widgetParams[key]); - } - - var widgetUniqueId = widgetParams.module + widgetParams.action; - currentWidgetLoading = widgetUniqueId; - - widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParams, function (response) { - // if the widget that was loaded was not for the latest clicked link, do nothing w/ the response - if (widgetUniqueId != currentWidgetLoading) { - return; - } - - loading.hide(); - report.css('display', 'inline-block').html($(response)); - - // scroll to report - piwikHelper.lazyScrollTo(report, 400); - }, function (deferred, status) { - if (status == 'abort' || !deferred || deferred.status < 400 || deferred.status >= 600) { - return; - } - - loading.hide(); - - var errorMessage = _pk_translate('General_ErrorRequest', ['', '']); - if ($('#loadingError').html()) { - errorMessage = $('#loadingError').html(); - } - - report.css('display', 'inline-block').html('<div class="dimensionLoadingError">' + errorMessage + '</div>'); - }); - }); }); }(jQuery)); diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js index 0561185de8..4bd5fb609a 100644 --- a/plugins/CoreHome/javascripts/dataTable.js +++ b/plugins/CoreHome/javascripts/dataTable.js @@ -1,3 +1,4 @@ + /*! * Piwik - free/libre analytics platform * diff --git a/plugins/CoreHome/javascripts/menu.js b/plugins/CoreHome/javascripts/menu.js deleted file mode 100644 index 2f5e16c827..0000000000 --- a/plugins/CoreHome/javascripts/menu.js +++ /dev/null @@ -1,114 +0,0 @@ -/*! - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -/** - * @constructor - */ -function menu() { - this.param = {}; -} - -menu.prototype = -{ - resetTimer: null, - - adaptSubMenuHeight: function() { - var subNavHeight = $('.sfHover > ul').outerHeight(); - $('.nav_sep').height(subNavHeight); - }, - - overMainLI: function () { - var $this = $(this); - $this.siblings().removeClass('sfHover'); - $this.addClass('sfHover'); - menu.prototype.adaptSubMenuHeight(); - clearTimeout(menu.prototype.resetTimer); - }, - - outMainLI: function () { - clearTimeout(menu.prototype.resetTimer); - menu.prototype.resetTimer = setTimeout(function() { - $('.Menu-tabList > .sfHover', this.menuNode).removeClass('sfHover'); - $('.Menu-tabList > .sfActive', this.menuNode).addClass('sfHover'); - menu.prototype.adaptSubMenuHeight(); - }, 2000); - }, - - onItemClick: function (e) { - if (e.which === 2) { - return; - } - $('.Menu--dashboard').trigger('piwikSwitchPage', this); - broadcast.propagateAjax( $(this).attr('href').substr(1) ); - return false; - }, - - init: function () { - this.menuNode = $('.Menu--dashboard'); - - this.menuNode.find("li:has(ul),li#Searchmenu").hover(this.overMainLI, this.outMainLI); - this.menuNode.find("li:has(ul),li#Searchmenu").focusin(this.overMainLI); - - this.menuNode.find('a.menuItem').click(this.onItemClick); - - menu.prototype.adaptSubMenuHeight(); - }, - - activateMenu: function (module, action, params) { - params = params || {}; - params.module = module; - params.action = action; - - this.menuNode.find('li').removeClass('sfHover').removeClass('sfActive'); - var $activeLink = this.menuNode.find('a').filter(function () { - var url = $(this).attr('href'); - if (!url) { - return false; - } - - for (var key in params) { - if (!params.hasOwnProperty(key) - || !params[key] - ) { - continue; - } - - var actual = broadcast.getValueFromHash(key, url); - if (actual != params[key]) { - return false; - } - } - - return true; - }); - - $activeLink.closest('li').addClass('sfHover'); - $activeLink.closest('li.menuTab').addClass('sfActive').addClass('sfHover'); - }, - - // getting the right li is a little tricky since goals uses idGoal, and overview is index. - getSubmenuID: function (module, id, action) { - var $li = ''; - // So, if module is Goals, id is present, and action is not Index, must be one of the goals - if ((module == 'Goals' || module == 'Ecommerce') && id != '' && (action != 'index')) { - $li = $("#" + module + "_" + action + "_" + id); - // if module is Dashboard and id is present, must be one of the dashboards - } else if (module == 'Dashboard') { - if (!id) id = 1; - $li = $("#" + module + "_" + action + "_" + id); - } else { - $li = $("#" + module + "_" + action); - } - return $li; - }, - - loadFirstSection: function () { - if (broadcast.isHashExists() == false) { - $('li:first a:first', this.menuNode).click().addClass('sfHover').addClass('sfActive'); - } - } -}; diff --git a/plugins/CoreHome/javascripts/menu_init.js b/plugins/CoreHome/javascripts/menu_init.js deleted file mode 100644 index 490c859185..0000000000 --- a/plugins/CoreHome/javascripts/menu_init.js +++ /dev/null @@ -1,19 +0,0 @@ -$(function () { - var isPageHasMenu = $('.Menu--dashboard').size(); - var isPageIsAdmin = $('#content.admin').size(); - if (isPageHasMenu) { - piwikMenu = new menu(); - piwikMenu.init(); - piwikMenu.loadFirstSection(); - } - - if(isPageIsAdmin) { - // don't use broadcast in admin pages - return; - } - if(isPageHasMenu) { - broadcast.init(); - } else { - broadcast.init(true); - } -}); diff --git a/plugins/CoreHome/javascripts/sparkline.js b/plugins/CoreHome/javascripts/sparkline.js index fc3b74f692..256e6f1589 100644 --- a/plugins/CoreHome/javascripts/sparkline.js +++ b/plugins/CoreHome/javascripts/sparkline.js @@ -35,14 +35,30 @@ piwik.initSparklines = function() { }; window.initializeSparklines = function () { - var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable']; + var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable', 'forceView', 'random']; - $("[data-graph-id]").each(function () { + $('.graphEvolution [data-report]').each(function () { var graph = $(this); - // try to find sparklines and add them clickable behaviour - graph.parent().find('div.sparkline').each(function () { + // we search for .widget to make sure eg in the Dashboard to not update any graph of another report + var selectorsToFindParent = ['.widget', '.reporting-page', 'body']; + var index = 0, selector, parent; + for (index; index < selectorsToFindParent.length; index++) { + selector = selectorsToFindParent[index]; + parent = graph.parents(selector).first(); + if (parent && parent.length) { + break; + } + } + + if (!parent || !parent.length) { + return; + } + var sparklines = parent.find('div.sparkline'); + + // try to find sparklines and add them clickable behaviour + sparklines.each(function () { // find the sparkline and get it's src attribute var sparklineUrl = $('img', this).attr('data-src'); @@ -66,8 +82,8 @@ window.initializeSparklines = function () { // on click, reload the graph with the new url $(this).off('click.sparkline'); $(this).on('click.sparkline', function () { - var reportId = graph.attr('data-graph-id'), - dataTable = $(require('piwik/UI').DataTable.getDataTableByReport(reportId)); + var reportId = graph.attr('data-report'), + dataTable = graph; // when the metrics picker is used, the id of the data table might be updated (which is correct behavior). // for example, in goal reports it might change from GoalsgetEvolutionGraph to GoalsgetEvolutionGraph1. diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json index a6bab4a544..7b3d4bbcf3 100644 --- a/plugins/CoreHome/lang/en.json +++ b/plugins/CoreHome/lang/en.json @@ -48,6 +48,7 @@ "YouAreUsingTheLatestVersion": "You are using the latest version of Piwik!", "ClickRowToExpandOrContract": "Click this row to expand or contract the subtable.", "UndoPivotBySubtable": "This report has been pivoted %s Undo pivot", - "PivotBySubtable": "This report is not pivoted %s Pivot by %s" + "PivotBySubtable": "This report is not pivoted %s Pivot by %s", + "NoSuchPage": "This page does not exist" } } diff --git a/plugins/CoreHome/stylesheets/coreHome.less b/plugins/CoreHome/stylesheets/coreHome.less index 5474eae3d2..63607d749f 100644 --- a/plugins/CoreHome/stylesheets/coreHome.less +++ b/plugins/CoreHome/stylesheets/coreHome.less @@ -120,6 +120,12 @@ div.ui-datepicker { display: none; } +.reporting-page { + .sparklines { + max-width: 1250px; + } +} + div .sparkline { float: left; clear: both; diff --git a/plugins/CoreHome/stylesheets/zen-mode.less b/plugins/CoreHome/stylesheets/zen-mode.less index 124c3395ff..112c6b2da6 100644 --- a/plugins/CoreHome/stylesheets/zen-mode.less +++ b/plugins/CoreHome/stylesheets/zen-mode.less @@ -77,13 +77,25 @@ #content:not(.admin), .widget { - h2:nth-of-type(n+2) { - margin-top: 40px; + .reporting-page { + .smallTopMargin:not(.graphEvolution) { + margin-top: 20px; + } } h2 { + margin-top: 40px; padding-left: 10px; font-size: 24px; + + &.noTopMargin { + margin-top: 0px; + } + } + + .widget [piwik-widget-container] [piwik-widget]:first-child h2 { + // eg Visits Overview with Graph should not have a margin-top + margin-top: 0px; } } diff --git a/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig b/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig deleted file mode 100644 index 57ec8279d5..0000000000 --- a/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig +++ /dev/null @@ -1,29 +0,0 @@ -<div class="reportsByDimensionView"> - - <div class="entityList"> - {% for category, dimensions in dimensionCategories %} - {% set firstCategory = (loop.index0 == 0) %} - <div class='dimensionCategory'> - {{ category|translate }} - <ul class='listCircle'> - {% for idx, dimension in dimensions %} - <li class="reportDimension {% if idx == 0 and firstCategory %}activeDimension{% endif %}" - data-url="{{ dimension.url }}"> - <span class='dimension'>{{ dimension.title|translate }}</span> - </li> - {% endfor %} - </ul> - </div> - {% endfor %} - </div> - - <div style="float:left;max-width:900px;"> - <div class="loadingPiwik" style="display:none;"> - <img src="plugins/Morpheus/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }} - </div> - - <div class="dimensionReport">{{ firstReport|raw }}</div> - </div> - <div class="clear"></div> - -</div> diff --git a/plugins/CoreHome/templates/_indexContent.twig b/plugins/CoreHome/templates/_indexContent.twig index 46d2f13579..671c26e782 100644 --- a/plugins/CoreHome/templates/_indexContent.twig +++ b/plugins/CoreHome/templates/_indexContent.twig @@ -12,8 +12,11 @@ {{ ajax.loadingDiv() }} + <div piwik-popover></div> + <div id="content" class="home"> {% if content %}{{ content }}{% endif %} + <div piwik-reporting-page ng-cloak></div> </div> <div class="clear"></div> </div> diff --git a/plugins/CoreHome/templates/getDefaultIndexView.twig b/plugins/CoreHome/templates/getDefaultIndexView.twig index 7b46ca3f76..4efd453159 100644 --- a/plugins/CoreHome/templates/getDefaultIndexView.twig +++ b/plugins/CoreHome/templates/getDefaultIndexView.twig @@ -4,9 +4,8 @@ {% include "@CoreHome/_siteSelectHeader.twig" %} -{% if (menu is defined and menu) %} - {% include "@CoreHome/_menu.twig" %} -{% endif %} +<div piwik-reporting-menu></div> +<div class="nav_sep"></div> {% include "@CoreHome/_indexContent.twig" %} diff --git a/plugins/CoreHome/templates/widgetContainer.twig b/plugins/CoreHome/templates/widgetContainer.twig new file mode 100755 index 0000000000..b3eda12997 --- /dev/null +++ b/plugins/CoreHome/templates/widgetContainer.twig @@ -0,0 +1,18 @@ +<div> + <div piwik-widget + containerid="{{ containerId|e('html_attr') }}" + widgetized="{% if isWidgetized %}true{% else %}false{% endif %}"></div> + + <script type="text/javascript"> + $(function () { + + var piwikWidget = $('[piwik-widget][containerid={{ containerId|e('js') }}]'); + + angular.element(document).injector().invoke(function($compile) { + var scope = angular.element(piwikWidget).scope(); + $compile(piwikWidget)(scope.$new()); + }); + + }); + </script> +</div>
\ No newline at end of file diff --git a/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php index 5675153c34..5114167364 100644 --- a/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php +++ b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php @@ -12,7 +12,6 @@ use Piwik\Archive\DataTableFactory; use Piwik\Common; use Piwik\DataTable; use Piwik\DataTable\Row; -use Piwik\Menu\MenuMain; use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator; use Piwik\Url; @@ -76,7 +75,6 @@ class Evolution extends JqplotDataGenerator $periodLabel = reset($dataTables)->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLabel(); $axisXOnClick = array(); - $queryStringAsHash = $this->getQueryStringAsHash(); foreach ($dataTable->getDataTables() as $metadataDataTable) { $dateInUrl = $metadataDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getDateStart(); $parameters = array( @@ -85,16 +83,7 @@ class Evolution extends JqplotDataGenerator 'date' => $dateInUrl->toString(), 'segment' => \Piwik\API\Request::getRawSegmentFromRequest() ); - $hash = ''; - if (!empty($queryStringAsHash)) { - $hash = '#' . Url::getQueryStringFromParameters($queryStringAsHash + $parameters); - } - $link = 'index.php?' . - Url::getQueryStringFromParameters(array( - 'module' => 'CoreHome', - 'action' => 'index', - ) + $parameters) - . $hash; + $link = Url::getQueryStringFromParameters($parameters); $axisXOnClick[] = $link; } $visualization->setAxisXOnClick($axisXOnClick); @@ -144,34 +133,6 @@ class Evolution extends JqplotDataGenerator return $label; } - /** - * We link the graph dots to the same report as currently being displayed (only the date would change). - * - * In some cases the widget is loaded within a report that doesn't exist as such. - * For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to. - * Instead, the graph must link back to the dashboard. - * - * In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report. - * - * To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded - * belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL, - * which results in loading the dashboard. - * - * @return array Query string array to append to the URL hash or false if the dashboard should be displayed - */ - private function getQueryStringAsHash() - { - $queryString = Url::getArrayFromCurrentQueryString(); - $piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY'); - foreach ($piwikParameters as $parameter) { - unset($queryString[$parameter]); - } - if (MenuMain::getInstance()->isUrlFound($queryString)) { - return $queryString; - } - return false; - } - private function isLinkEnabled() { static $linkEnabled; diff --git a/plugins/CoreVisualizations/Visualizations/Sparkline.php b/plugins/CoreVisualizations/Visualizations/Sparkline.php index 2ca75bbcfe..3c1dbf5a56 100644 --- a/plugins/CoreVisualizations/Visualizations/Sparkline.php +++ b/plugins/CoreVisualizations/Visualizations/Sparkline.php @@ -25,7 +25,7 @@ class Sparkline extends ViewDataTable * @see ViewDataTable::main() * @return mixed */ - protected function buildView() + public function render() { // If period=range, we force the sparkline to draw daily data points $period = Common::getRequestVar('period'); @@ -58,7 +58,7 @@ class Sparkline extends ViewDataTable $graph->main(); - return $graph; + return $graph->render(); } /** diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines.php b/plugins/CoreVisualizations/Visualizations/Sparklines.php new file mode 100644 index 0000000000..3b576c885e --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/Sparklines.php @@ -0,0 +1,147 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CoreVisualizations\Visualizations; + +use Piwik\DataTable; +use Piwik\Metrics; +use Piwik\Plugin\ViewDataTable; +use Piwik\Url; +use Piwik\View; + +/** + * Reads the requested DataTable from the API and prepares data for the Sparklines view. It can display any amount + * of sparklines. Within a reporting page sparklines are shown in 2 columns, in a dashboard or when exported as a widget + * the sparklines are shown in one column. + * + * The sparklines view currently only supports requesting columns from the same API (the API method of the defining + * report) via {Sparklines\Config::addSparklineMetric($columns = array('nb_visits', 'nb_unique_visitors'))}. + * + * Example: + * $view->config->addSparklineMetric('nb_visits'); // if an array of metrics given, they will be displayed comma separated + * $view->config->addTranslation('nb_visits', 'Visits'); + * Results in: [sparkline image] X visits + * Data is fetched from the configured $view->requestConfig->apiMethodToRequestDataTable. + * + * In case you want to add any custom sparklines from any other API method you can call + * {@link Sparklines\Config::addSparkline()}. + * + * Example: + * $sparklineUrlParams = array('columns' => array('nb_visits)); + * $evolution = array('currentValue' => 5, 'pastValue' => 10, 'tooltip' => 'Foo bar'); + * $view->config->addSparkline($sparklineUrlParams, $value = 5, $description = 'Visits', $evolution); + * + * @property Sparklines\Config $config + */ +class Sparklines extends ViewDataTable +{ + const ID = 'sparklines'; + + public static function getDefaultConfig() + { + return new Sparklines\Config(); + } + + /** + * @see ViewDataTable::main() + * @return mixed + */ + public function render() + { + $view = new View('@CoreVisualizations/_dataTableViz_sparklines.twig'); + + $columnsList = array(); + if ($this->config->hasSparklineMetrics()) { + foreach ($this->config->getSparklineMetrics() as $cols) { + $columnsList = array_merge($cols['columns'], $columnsList); + } + } + + $this->requestConfig->request_parameters_to_modify['columns'] = $columnsList; + $this->requestConfig->request_parameters_to_modify['format_metrics'] = '1'; + + if (!empty($this->requestConfig->apiMethodToRequestDataTable)) { + $this->fetchConfiguredSparklines(); + } + + $view->sparklines = $this->config->getSortedSparklines(); + + return $view->render(); + } + + private function fetchConfiguredSparklines() + { + $data = $this->loadDataTableFromAPI(); + + $this->applyFilters($data); + + if (!$this->config->hasSparklineMetrics()) { + foreach ($data->getColumns() as $column) { + $this->config->addSparklineMetric($column); + } + } + + $translations = $this->config->translations; + + $firstRow = $data->getFirstRow(); + + foreach ($this->config->getSparklineMetrics() as $sparklineMetric) { + $column = $sparklineMetric['columns']; + $order = $sparklineMetric['order']; + + if ($column === 'label') { + continue; + } + + if (empty($column)) { + $this->config->addPlaceholder($order); + continue; + } + + if (!is_array($column)) { + $column = array($column); + } + + $values = array(); + $descriptions = array(); + + foreach ($column as $col) { + $value = $firstRow->getColumn($col); + + if ($value === false) { + $value = 0; + } + + $values[] = $value; + $descriptions[] = isset($translations[$col]) ? $translations[$col] : $col; + } + + $sparklineUrlParams = array( + 'columns' => $column, + 'module' => $this->requestConfig->getApiModuleToRequest(), + 'action' => $this->requestConfig->getApiMethodToRequest() + ); + + $this->config->addSparkline($sparklineUrlParams, $values, $descriptions, null, $order); + } + } + + private function applyFilters(DataTable\DataTableInterface $table) + { + foreach ($this->config->getPriorityFilters() as $filter) { + $table->filter($filter[0], $filter[1]); + } + + // queue other filters so they can be applied later if queued filters are disabled + foreach ($this->config->getPresentationFilters() as $filter) { + $table->queueFilter($filter[0], $filter[1]); + } + + $table->applyQueuedFilters(); + } +} diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php b/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php new file mode 100644 index 0000000000..ca54a6d564 --- /dev/null +++ b/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php @@ -0,0 +1,354 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\Common; +use Piwik\DataTable\Filter\CalculateEvolutionFilter; +use Piwik\Metrics; +use Piwik\NoAccessException; +use Piwik\Period\Range; +use Piwik\Site; +use Piwik\Url; + +/** + * DataTable Visualization that derives from Sparklines. + */ +class Config extends \Piwik\ViewDataTable\Config +{ + /** + * Holds metrics / column names that will be used to fetch data from the configured $requestConfig API. + * Default value: array + */ + private $sparkline_metrics = array(); + + /** + * Holds the actual sparkline entries based on fetched data that will be used in the template. + * @var array + */ + private $sparklines = array(); + + public function __construct() + { + parent::__construct(); + + $this->translations = Metrics::getDefaultMetricTranslations(); + } + + /** + * @ignore + * @return array + */ + public function getSparklineMetrics() + { + return $this->sparkline_metrics; + } + + /** + * @ignore + * @return bool + */ + public function hasSparklineMetrics() + { + return !empty($this->sparkline_metrics); + } + + /** + * Removes an existing sparkline entry. Especially useful in dataTable filters in case sparklines should be not + * displayed depending on the fetched data. + * + * Example: + * $config->addSparklineMetric('nb_users'); + * $config->filters[] = function ($dataTable) use ($config) { + * if ($dataTable->getFirstRow()->getColumn('nb_users') == 0) { + * // do not show a sparkline if there are no recorded users + * $config->removeSparklineMetric('nb_users'); + * } + * } + * + * @param array|string $metricNames The name of the metrics in the same format they were used when added via + * {@link addSparklineMetric} + */ + public function removeSparklineMetric($metricNames) + { + foreach ($this->sparkline_metrics as $index => $metric) { + if ($metric['columns'] === $metricNames) { + array_splice($this->sparkline_metrics, $index, 1); + + break; + } + } + } + + /** + * Replaces an existing sparkline entry with different columns. Especially useful in dataTable filters in case + * sparklines should be not displayed depending on the fetched data. + * + * Example: + * $config->addSparklineMetric('nb_users'); + * $config->filters[] = function ($dataTable) use ($config) { + * if ($dataTable->getFirstRow()->getColumn('nb_users') == 0) { + * // instead of showing the sparklines for users, show a placeholder if there are no recorded users + * $config->replaceSparklineMetric(array('nb_users'), ''); + * } + * } + * + * @param array|string $metricNames The name of the metrics in the same format they were used when added via + * {@link addSparklineMetric} + * @param array|string $replacementColumns The removed columns will be replaced with these columns + */ + public function replaceSparklineMetric($metricNames, $replacementColumns) + { + foreach ($this->sparkline_metrics as $index => $metric) { + if ($metric['columns'] === $metricNames) { + $this->sparkline_metrics[$index]['columns'] = $replacementColumns; + } + } + } + + /** + * Adds a new sparkline. + * + * It will show a sparkline image, the value of the resolved metric name and a descrption. Optionally, multiple + * values can be shown after a sparkline image by passing multiple metric names + * (eg array('nb_visits', 'nb_actions')). The data will be requested from the configured api method see + * {@link Piwik\ViewDataTable\RequestConfig::$apiMethodToRequestDataTable}. + * + * Example: + * $config->addSparklineMetric('nb_visits'); + * $config->addTranslation('nb_visits', 'Visits'); + * Results in: [sparkline image] X visits + * + * Example: + * $config->addSparklineMetric(array('nb_visits', 'nb_actions')); + * $config->addTranslations(array('nb_visits' => 'Visits', 'nb_actions' => 'Actions')); + * Results in: [sparkline image] X visits, Y actions + * + * @param string|array $metricName Either one metric name (eg 'nb_visits') or an array of metric names + * @param int|null $order Defines the order. The lower the order the earlier the sparkline will be displayed. + * By default the sparkline will be appended to the end. + */ + public function addSparklineMetric($metricName, $order = null) + { + $this->sparkline_metrics[] = array( + 'columns' => $metricName, + 'order' => $order + ); + } + + /** + * Adds a placeholder. In this case nothing will be shown, neither a sparkline nor any description. This can be + * useful if you want to have some kind of separator. Eg if you want to have a sparkline on the left side but + * not sparkline on the right side. + * + * @param int|null $order Defines the order. The lower the order the earlier the sparkline will be displayed. + * By default the sparkline will be appended to the end. + */ + public function addPlaceholder($order = null) + { + $this->sparklines[] = array( + 'url' => '', + 'metrics' => array(), + 'order' => $this->getSparklineOrder($order) + ); + } + + /** + * Add a new sparkline to be displayed to the view. + * + * Each sparkline can consist of one or multiple metrics. One metric consists of a value and a description. By + * default the value is shown first, then the description. The description can optionally contain a '%s' in case + * the value shall be displayed within the description. If multiple metrics are given, they will be separated by + * a comma. + * + * @param array $requestParamsForSparkline You need to at least set a module / action eg + * array('columns' => array('nb_visit'), 'module' => '', 'action' => '') + * @param int|float|string|array $value Either the metric value or an array of values. + * @param string|array $description Either one description or an array of descriptions. If an array, both + * $value and $description need the same amount of array entries. + * $description[0] should be the description for $value[0]. + * $description should be already translated. If $value should appear + * somewhere within the text a `%s` can be used in the translation. + * @param array|null $evolution Optional array containing at least the array keys 'currentValue' and + * 'pastValue' which are needed to calculate the correct percentage. + * An optional 'tooltip' can be set as well. Eg + * array('currentValue' => 10, 'pastValue' => 20, + * 'tooltip' => '10 visits in 2015-07-26 compared to 20 visits in 2015-07-25') + * @param int $order Defines the order. The lower the order the earlier the sparkline will be + * displayed. By default the sparkline will be appended to the end. + * @throws \Exception In case an evolution parameter is set but has wrong data structure + */ + public function addSparkline($requestParamsForSparkline, $value, $description, $evolution = null, $order = null) + { + $metrics = array(); + + if (is_array($value)) { + $values = $value; + } else { + $values = array($value); + } + + if (!is_array($description)) { + $description = array($description); + } + + if (count($values) === count($description)) { + foreach ($values as $index => $value) { + $metrics[] = array( + 'value' => $value, + 'description' => $description[$index] + ); + } + } else { + $msg = 'The number of values and descriptions need to be the same to add a sparkline. '; + $msg .= 'Values: ' . implode(', ', $values). ' Descriptions: ' . implode(', ', $description); + throw new \Exception($msg); + } + + if (empty($metrics)) { + return; + } + + $sparkline = array( + 'url' => $this->getUrlSparkline($requestParamsForSparkline), + 'metrics' => $metrics, + 'order' => $this->getSparklineOrder($order) + ); + + if (!empty($evolution)) { + if (!is_array($evolution) || + !array_key_exists('currentValue', $evolution) || + !array_key_exists('pastValue', $evolution)) { + throw new \Exception('In order to show an evolution in the sparklines view a currentValue and pastValue array key needs to be present'); + } + + $evolutionPercent = CalculateEvolutionFilter::calculate($evolution['currentValue'], $evolution['pastValue'], $precision = 1); + + // do not display evolution if evolution percent is 0 and current value is 0 + if ($evolutionPercent != 0 || $evolution['currentValue'] != 0) { + $sparkline['evolution'] = array( + 'percent' => $evolutionPercent, + 'tooltip' => !empty($evolution['tooltip']) ? $evolution['tooltip'] : null + ); + } + + } + + $this->sparklines[] = $sparkline; + } + + /** + * @return array + * @ignore + */ + public function getSortedSparklines() + { + usort($this->sparklines, function ($a, $b) { + if ($a['order'] == $b['order']) { + return 0; + } + return ($a['order'] < $b['order']) ? -1 : 1; + }); + + return $this->sparklines; + } + + private function getSparklineOrder($order) + { + if (!isset($order)) { + // make sure to append to the end if nothing set (in the order they are added) + $order = 999 + count($this->sparklines); + } + + return (int) $order; + } + + /** + * Returns a URL to a sparkline image for a report served by the current plugin. + * + * The result of this URL should be used with the [sparkline()](/api-reference/Piwik/View#twig) twig function. + * + * The current site ID and period will be used. + * + * @param array $customParameters The array of query parameter name/value pairs that + * should be set in result URL. + * @return string The generated URL. + */ + private function getUrlSparkline($customParameters = array()) + { + $customParameters['viewDataTable'] = 'sparkline'; + + $params = $this->getGraphParamsModified($customParameters); + + // convert array values to comma separated + foreach ($params as &$value) { + if (is_array($value)) { + $value = rawurlencode(implode(',', $value)); + } + } + $url = Url::getCurrentQueryStringWithParametersModified($params); + return $url; + } + + /** + * Returns the array of new processed parameters once the parameters are applied. + * For example: if you set range=last30 and date=2008-03-10, + * the date element of the returned array will be "2008-02-10,2008-03-10" + * + * Parameters you can set: + * - range: last30, previous10, etc. + * - date: YYYY-MM-DD, today, yesterday + * - period: day, week, month, year + * + * @param array $paramsToSet array( 'date' => 'last50', 'viewDataTable' =>'sparkline' ) + * @throws \Piwik\NoAccessException + * @return array + */ + private function getGraphParamsModified($paramsToSet = array()) + { + if (!isset($paramsToSet['period'])) { + $period = Common::getRequestVar('period'); + } else { + $period = $paramsToSet['period']; + } + + if ($period == 'range') { + return $paramsToSet; + } + + if (!isset($paramsToSet['range'])) { + $range = 'last30'; + } else { + $range = $paramsToSet['range']; + } + + if (!isset($paramsToSet['idSite'])) { + $idSite = Common::getRequestVar('idSite'); + } else { + $idSite = $paramsToSet['idSite']; + } + + if (!isset($paramsToSet['date'])) { + $endDate = Common::getRequestVar('date', 'yesterday', 'string'); + } else { + $endDate = $paramsToSet['date']; + } + + $site = new Site($idSite); + + if (is_null($site)) { + throw new NoAccessException("Website not initialized, check that you are logged in and/or using the correct token_auth."); + } + + $paramDate = Range::getRelativeToEndDate($period, $range, $endDate, $site); + + $params = array_merge($paramsToSet, array('date' => $paramDate)); + return $params; + } + +} diff --git a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js index 3b62e9197e..b77add4962 100644 --- a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js +++ b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js @@ -90,36 +90,7 @@ && typeof self.jqplotParams.axes.xaxis.onclick[lastTick] == 'string') { var url = self.jqplotParams.axes.xaxis.onclick[lastTick]; - if (url && -1 === url.indexOf('#')) { - var module = broadcast.getValueFromHash('module'); - var action = broadcast.getValueFromHash('action'); - var idGoal = broadcast.getValueFromHash('idGoal'); - var idSite = broadcast.getValueFromUrl('idSite', url); - var period = broadcast.getValueFromUrl('period', url); - var date = broadcast.getValueFromUrl('date', url); - - if (module && action) { - url += '#module=' + module + '&action=' + action; - - if (idSite) { - url += '&idSite=' + idSite; - } - - if (idGoal) { - url += '&idGoal=' + idGoal; - } - - if (period) { - url += '&period=' + period; - } - - if (period) { - url += '&date=' + date; - } - } - } - - piwikHelper.redirectToUrl(url); + broadcast.propagateNewPage(url); } }) .on('jqplotPiwikTickOver', function (e, tick) { @@ -161,6 +132,10 @@ render: function () { JqplotGraphDataTablePrototype.render.call(this); + + if (initializeSparklines) { + initializeSparklines(); + } } }); diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig new file mode 100644 index 0000000000..359c0f9768 --- /dev/null +++ b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig @@ -0,0 +1,31 @@ +{% import '@CoreVisualizations/macros.twig' as macros %} + +{% if not isWidget %} +<div class="row"> + <div class="col-md-6"> +{% endif %} + + {% for key, sparkline in sparklines %} + {% if key is even %} + {{ macros.singleSparkline(sparkline) }} + {% endif %} + {% endfor %} + +{% if not isWidget %} + </div> + <div class="col-md-6"> +{% endif %} + + {% for key, sparkline in sparklines %} + {% if key is odd %} + {{ macros.singleSparkline(sparkline) }} + {% endif %} + {% endfor %} + +{% if not isWidget %} + </div> +</div> +{% endif %} + +{% include "_sparklineFooter.twig" %} + diff --git a/plugins/CoreVisualizations/templates/macros.twig b/plugins/CoreVisualizations/templates/macros.twig new file mode 100644 index 0000000000..ffd1885be1 --- /dev/null +++ b/plugins/CoreVisualizations/templates/macros.twig @@ -0,0 +1,32 @@ +{% macro singleSparkline(sparkline) %} + <div class="sparkline"> + {% if sparkline.url %}{{ sparkline(sparkline.url)|raw }}{% endif %} + {% for metric in sparkline.metrics %} + {% if '%s' in metric.description -%} + {{ metric.description|translate("<strong>"~metric.value~"</strong>")|raw }} + {%- else %} + <strong>{{ metric.value }}</strong> {{ metric.description }} + {%- endif %}{% if not loop.last %}, {% endif %} + {% endfor %} + {% if sparkline.evolution is defined %} + + {% set evolutionPretty = sparkline.evolution.percent %} + + {% if sparkline.evolution.percent < 0 %} + {% set evolutionClass = 'negative-evolution' %} + {% set evolutionIcon = 'arrow_down.png' %} + {% elseif sparkline.evolution.percent == 0 %} + {% set evolutionClass = 'neutral-evolution' %} + {% set evolutionIcon = 'stop.png' %} + {% else %} + {% set evolutionClass = 'positive-evolution' %} + {% set evolutionIcon = 'arrow_up.png' %} + {% set evolutionPretty = '+' ~ sparkline.evolution.percent %} + {% endif %} + + <span class="metricEvolution" title="{{ sparkline.evolution.tooltip }}"><img + style="padding-right:4px" src="plugins/MultiSites/images/{{ evolutionIcon }}"/> + <strong class="{{ evolutionClass }}">{{ evolutionPretty }}</strong></span> + {% endif %} + </div> +{% endmacro %} diff --git a/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php b/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php new file mode 100644 index 0000000000..3541344f04 --- /dev/null +++ b/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php @@ -0,0 +1,128 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CoreVisualizations\tests\Integration; + +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines\Config; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group CoreVisualizations + * @group SparklinesConfigTest + * @group Plugins + */ +class SparklinesConfigTest extends IntegrationTestCase +{ + /** + * @var Config + */ + private $config; + + public function setUp() + { + parent::setUp(); + FakeAccess::$superUser = true; + + if (!Fixture::siteCreated(1)) { + Fixture::createWebsite('2014-01-01 00:00:00'); + } + + $this->config = new Config(); + } + + public function test_addSparkline_shouldAddAMinimalSparklineWithOneValueAndUseDefaultOrder() + { + $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits'); + + $expectedSparkline = array( + 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline', + 'metrics' => array ( + array ('value' => 10, 'description' => 'Visits'), + ), + 'order' => 999 + ); + + $this->assertSame(array($expectedSparkline), $this->config->getSortedSparklines()); + } + + public function test_addSparkline_shouldAddSparklineWithMultipleValues() + { + $this->config->addSparkline($this->sparklineParams(), $values = array(10, 20), $description = array('Visits', 'Actions')); + + $sparklines = $this->config->getSortedSparklines(); + + $this->assertSame(array ( + array ('value' => 10, 'description' => 'Visits'), + array ('value' => 20, 'description' => 'Actions'), + ), $sparklines[0]['metrics']); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Values: 10, 20, 30 Descriptions: Visits, Actions + */ + public function test_addSparkline_shouldThrowAnException_IfValuesDoesNotMatchAmountOfDescriptions() + { + $this->config->addSparkline($this->sparklineParams(), $values = array(10, 20, 30), $description = array('Visits', 'Actions')); + } + + public function test_addSparkline_shouldAddEvolution() + { + $evolution = array('currentValue' => 10, 'pastValue' => 21, + 'tooltip' => '1 visit compared to 2 visits'); + $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits', $evolution); + + $sparklines = $this->config->getSortedSparklines(); + + $this->assertSame(array ( + 'percent' => '-52.4%', + 'tooltip' => '1 visit compared to 2 visits' + ), $sparklines[0]['evolution']); + } + + public function test_addSparkline_shouldAddOrder() + { + $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits', $evolution = null, $order = '42'); + + $sparklines = $this->config->getSortedSparklines(); + + $this->assertSame(42, $sparklines[0]['order']); + } + + public function test_addSparkline_shouldBeAbleToBuildSparklineUrlBasedOnGETparams() + { + $oldGet = $_GET; + $_GET = $this->sparklineParams(); + $this->config->addSparkline(array('columns' => 'nb_visits'), $value = 10, $description = 'Visits'); + $_GET = $oldGet; + + $sparklines = $this->config->getSortedSparklines(); + + $this->assertSame('?columns=nb_visits&viewDataTable=sparkline&date=2012-03-06,2012-04-04', $sparklines[0]['url']); + } + + private function sparklineParams($params = array()) + { + $params['period'] = 'day'; + $params['date'] = '2012-04-04'; + $params['idSite'] = '1'; + $params['module'] = 'CoreHome'; + $params['action'] = 'renderMe'; + + return $params; + } + + public function provideContainerConfig() + { + return array( + 'Piwik\Access' => new FakeAccess() + ); + } +} diff --git a/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php b/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php new file mode 100644 index 0000000000..516f19f8e1 --- /dev/null +++ b/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php @@ -0,0 +1,130 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CoreVisualizations\tests\Unit; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines\Config; + +/** + * @group CoreVisualizations + * @group SparklinesConfigTest + * @group Sparklines + * @group Plugins + */ +class SparklinesConfigTest extends \PHPUnit_Framework_TestCase +{ + /** + * @var Config + */ + private $config; + + public function setUp() + { + $this->config = new Config(); + } + + public function test_hasSparklineMetrics_shouldNotHaveSparklineMetrics_ByDefault() + { + $this->assertFalse($this->config->hasSparklineMetrics()); + } + + public function test_hasSparklineMetrics_shouldHaveSparklineMetrics_IfAtLeastOneWasAdded() + { + $this->config->addSparklineMetric('nb_visits'); + + $this->assertTrue($this->config->hasSparklineMetrics()); + } + + public function test_getSparklineMetrics_shouldNotHaveSparklineMetrics_ByDefault() + { + $this->assertSame(array(), $this->config->getSparklineMetrics()); + } + + public function test_addSparklineMetric_getSparklineMetrics_shouldReturnAllAddedSparklineMetrics() + { + $this->addFewSparklines(); + + $this->assertSame(array( + array('columns' => 'nb_visits', 'order' => null), + array('columns' => 'nb_unique_visitors', 'order' => 99), + array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null), + ), $this->config->getSparklineMetrics()); + } + + public function test_removeSparklineMetric_shouldRemoveMetric_IfOnlySingleMetricIsGiven() + { + $this->addFewSparklines(); + + $this->config->removeSparklineMetric('nb_unique_visitors'); + + $this->assertSame(array( + array('columns' => 'nb_visits', 'order' => null), + array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null), + ), $this->config->getSparklineMetrics()); + } + + public function test_removeSparklineMetric_shouldRemoveMetric_IfMultipleMetricsAreGiven() + { + $this->addFewSparklines(); + + $this->config->removeSparklineMetric(array('nb_downloads', 'nb_outlinks')); + + $this->assertSame(array( + array('columns' => 'nb_visits', 'order' => null), + array('columns' => 'nb_unique_visitors', 'order' => 99), + ), $this->config->getSparklineMetrics()); + } + + public function test_replaceSparklineMetric_shouldBeAbleToReplaceColumns_IfSingleMetricIsGiven() + { + $this->addFewSparklines(); + + $this->config->replaceSparklineMetric('nb_unique_visitors', ''); + + $this->assertSame(array( + array('columns' => 'nb_visits', 'order' => null), + array('columns' => '', 'order' => 99), + array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null), + ), $this->config->getSparklineMetrics()); + } + + public function test_replaceSparklineMetric_shouldBeAbleToReplaceColumns_IfMultipleMetricsAreGiven() + { + $this->addFewSparklines(); + + $this->config->replaceSparklineMetric(array('nb_downloads', 'nb_outlinks'), ''); + + $this->assertSame(array( + array('columns' => 'nb_visits', 'order' => null), + array('columns' => 'nb_unique_visitors', 'order' => 99), + array('columns' => '', 'order' => null), + ), $this->config->getSparklineMetrics()); + } + + public function test_addPlaceholder_getSortedSparklines() + { + $this->config->addPlaceholder(); + $this->config->addPlaceholder($order = 10); + $this->config->addPlaceholder(); + $this->config->addPlaceholder($order = 3); + + $this->assertSame(array( + array('url' => '', 'metrics' => array(), 'order' => 3), + array('url' => '', 'metrics' => array(), 'order' => 10), + array('url' => '', 'metrics' => array(), 'order' => 999), + array('url' => '', 'metrics' => array(), 'order' => 1001), + ), $this->config->getSortedSparklines()); + } + + private function addFewSparklines() + { + $this->config->addSparklineMetric('nb_visits'); + $this->config->addSparklineMetric('nb_unique_visitors', 99); + $this->config->addSparklineMetric(array('nb_downloads', 'nb_outlinks')); + } + +} diff --git a/plugins/CustomVariables/Categories/CustomVariablesSubcategory.php b/plugins/CustomVariables/Categories/CustomVariablesSubcategory.php new file mode 100644 index 0000000000..d81b56568c --- /dev/null +++ b/plugins/CustomVariables/Categories/CustomVariablesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\CustomVariables\Categories; + +use Piwik\Category\Subcategory; + +class CustomVariablesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'CustomVariables_CustomVariables'; + protected $order = 10; + +} diff --git a/plugins/CustomVariables/Reports/Base.php b/plugins/CustomVariables/Reports/Base.php index be48bf4d44..0c6bcbec0b 100644 --- a/plugins/CustomVariables/Reports/Base.php +++ b/plugins/CustomVariables/Reports/Base.php @@ -14,7 +14,7 @@ abstract class Base extends \Piwik\Plugin\Report protected function init() { - $this->category = 'General_Visitors'; + $this->categoryId = 'General_Visitors'; } } diff --git a/plugins/CustomVariables/Reports/GetCustomVariables.php b/plugins/CustomVariables/Reports/GetCustomVariables.php index 2ddedc4e97..c6b28b6562 100644 --- a/plugins/CustomVariables/Reports/GetCustomVariables.php +++ b/plugins/CustomVariables/Reports/GetCustomVariables.php @@ -23,8 +23,8 @@ class GetCustomVariables extends Base array('<br />', '<a href="http://piwik.org/docs/custom-variables/" rel="noreferrer" target="_blank">', '</a>')); $this->actionToLoadSubTables = 'getCustomVariablesValuesFromNameId'; $this->order = 10; - $this->widgetTitle = 'CustomVariables_CustomVariables'; - $this->menuTitle = 'CustomVariables_CustomVariables'; + + $this->subcategoryId = 'CustomVariables_CustomVariables'; $this->hasGoalMetrics = true; } diff --git a/plugins/DBStats/Reports/GetMetricDataSummary.php b/plugins/DBStats/Reports/GetMetricDataSummary.php index 92b93fce9b..94735c4e03 100644 --- a/plugins/DBStats/Reports/GetMetricDataSummary.php +++ b/plugins/DBStats/Reports/GetMetricDataSummary.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; +use Piwik\Plugin\Reports; /** * Shows a datatable that displays the amount of space each numeric archive table @@ -34,7 +35,7 @@ class GetMetricDataSummary extends Base public function getRelatedReports() { return array( - self::factory('DBStats', 'getMetricDataSummaryByYear'), + Reports::factory('DBStats', 'getMetricDataSummaryByYear'), ); } diff --git a/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php b/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php index bc5e8e5c3c..81b89d5d99 100644 --- a/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php +++ b/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; +use Piwik\Plugin\Reports; /** * Shows a datatable that displays the amount of space each numeric archive table @@ -35,7 +36,7 @@ class GetMetricDataSummaryByYear extends Base public function getRelatedReports() { return array( - self::factory('DBStats', 'getMetricDataSummary'), + Reports::factory('DBStats', 'getMetricDataSummary'), ); } diff --git a/plugins/DBStats/Reports/GetReportDataSummary.php b/plugins/DBStats/Reports/GetReportDataSummary.php index d38f3ee717..58c1b065c5 100644 --- a/plugins/DBStats/Reports/GetReportDataSummary.php +++ b/plugins/DBStats/Reports/GetReportDataSummary.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; +use Piwik\Plugin\Reports; /** * Shows a datatable that displays the amount of space each blob archive table @@ -34,7 +35,7 @@ class GetReportDataSummary extends Base public function getRelatedReports() { return array( - self::factory('DBStats', 'getReportDataSummaryByYear'), + Reports::factory('DBStats', 'getReportDataSummaryByYear'), ); } } diff --git a/plugins/DBStats/Reports/GetReportDataSummaryByYear.php b/plugins/DBStats/Reports/GetReportDataSummaryByYear.php index 1bbe67e467..7ff78a638b 100644 --- a/plugins/DBStats/Reports/GetReportDataSummaryByYear.php +++ b/plugins/DBStats/Reports/GetReportDataSummaryByYear.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; +use Piwik\Plugin\Reports; /** * Shows a datatable that displays the amount of space each blob archive table @@ -35,7 +36,7 @@ class GetReportDataSummaryByYear extends Base public function getRelatedReports() { return array( - self::factory('DBStats', 'getReportDataSummary'), + Reports::factory('DBStats', 'getReportDataSummary'), ); } diff --git a/plugins/Dashboard/API.php b/plugins/Dashboard/API.php index f4637e862a..7bd5bd73a5 100644 --- a/plugins/Dashboard/API.php +++ b/plugins/Dashboard/API.php @@ -8,7 +8,6 @@ namespace Piwik\Plugins\Dashboard; use Piwik\Piwik; -use Piwik\WidgetsList; /** * This API is the <a href='http://piwik.org/docs/analytics-api/reference/' rel='noreferrer' target='_blank'>Dashboard API</a>: it gives information about dashboards. @@ -43,25 +42,22 @@ class API extends \Piwik\Plugin\API /** * Get the default dashboard. - * - * @return array[] + * @return \array[] */ private function getDefaultDashboard() { $defaultLayout = $this->dashboard->getDefaultLayout(); $defaultLayout = $this->dashboard->decodeLayout($defaultLayout); + $defaultDashboard = array('name' => Piwik::translate('Dashboard_Dashboard'), 'layout' => $defaultLayout, 'iddashboard' => 1); - $defaultDashboard = array('name' => Piwik::translate('Dashboard_Dashboard'), 'layout' => $defaultLayout); - - $widgets = $this->getExistingWidgetsWithinDashboard($defaultDashboard); + $widgets = $this->getVisibleWidgetsWithinDashboard($defaultDashboard); return $this->buildDashboard($defaultDashboard, $widgets); } /** * Get all dashboards which a user has created. - * - * @return array[] + * @return \array[] */ private function getUserDashboards() { @@ -71,17 +67,14 @@ class API extends \Piwik\Plugin\API $dashboards = array(); foreach ($userDashboards as $userDashboard) { - - if ($this->hasDashboardColumns($userDashboard)) { - $widgets = $this->getExistingWidgetsWithinDashboard($userDashboard); - $dashboards[] = $this->buildDashboard($userDashboard, $widgets); - } + $widgets = $this->getVisibleWidgetsWithinDashboard($userDashboard); + $dashboards[] = $this->buildDashboard($userDashboard, $widgets); } return $dashboards; } - private function getExistingWidgetsWithinDashboard($dashboard) + private function getVisibleWidgetsWithinDashboard($dashboard) { $columns = $this->getColumnsFromDashboard($dashboard); @@ -91,7 +84,7 @@ class API extends \Piwik\Plugin\API foreach ($columns as $column) { foreach ($column as $widget) { - if ($this->widgetIsNotHidden($widget) && $this->widgetExists($widget)) { + if ($this->widgetIsNotHidden($widget) && !empty($widget->parameters->module)) { $module = $widget->parameters->module; $action = $widget->parameters->action; @@ -105,39 +98,24 @@ class API extends \Piwik\Plugin\API private function getColumnsFromDashboard($dashboard) { - if (is_array($dashboard['layout'])) { - - return $dashboard['layout']; + if (empty($dashboard['layout'])) { + return array(); } - return $dashboard['layout']->columns; - } - - private function hasDashboardColumns($dashboard) - { if (is_array($dashboard['layout'])) { + return $dashboard['layout']; + } - return !empty($dashboard['layout']); + if (!empty($dashboard['layout']->columns)) { + return $dashboard['layout']->columns; } - return !empty($dashboard['layout']->columns); + return array(); } private function buildDashboard($dashboard, $widgets) { - return array('name' => $dashboard['name'], 'widgets' => $widgets); - } - - private function widgetExists($widget) - { - if (empty($widget->parameters->module)) { - return false; - } - - $module = $widget->parameters->module; - $action = $widget->parameters->action; - - return WidgetsList::isDefined($module, $action); + return array('name' => $dashboard['name'], 'id' => $dashboard['iddashboard'], 'widgets' => $widgets); } private function widgetIsNotHidden($widget) diff --git a/plugins/Dashboard/Categories/DashboardCategory.php b/plugins/Dashboard/Categories/DashboardCategory.php new file mode 100644 index 0000000000..99a3785c84 --- /dev/null +++ b/plugins/Dashboard/Categories/DashboardCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Dashboard\Categories; + +use Piwik\Category\Category; + +class DashboardCategory extends Category +{ + protected $id = 'Dashboard_Dashboard'; + protected $order = 0; +} diff --git a/plugins/Dashboard/Controller.php b/plugins/Dashboard/Controller.php index d77c283273..bef5a89237 100644 --- a/plugins/Dashboard/Controller.php +++ b/plugins/Dashboard/Controller.php @@ -13,8 +13,6 @@ use Piwik\Db; use Piwik\Piwik; use Piwik\Session\SessionNamespace; use Piwik\View; -use Piwik\WidgetsList; -use Piwik\FrontController; /** * Dashboard Controller @@ -39,24 +37,21 @@ class Controller extends \Piwik\Plugin\Controller $view = new View($template); $this->setGeneralVariablesView($view); - $view->availableWidgets = json_encode(WidgetsList::get()); $view->availableLayouts = $this->getAvailableLayouts(); $view->dashboardId = Common::getRequestVar('idDashboard', 1, 'int'); - // get the layout via FrontController so controller events are posted - $view->dashboardLayout = FrontController::getInstance()->dispatch('Dashboard', 'getDashboardLayout', - array($checkToken = false)); - return $view; } + // this public function embeddedIndex() { $view = $this->_getDashboardView('@Dashboard/embeddedIndex'); return $view->render(); } + // this is the exported widget public function index() { $view = $this->_getDashboardView('@Dashboard/index'); @@ -70,14 +65,6 @@ class Controller extends \Piwik\Plugin\Controller return $view->render(); } - public function getAvailableWidgets() - { - $this->checkTokenInUrl(); - - Json::sendHeaderJSON(); - return json_encode(WidgetsList::get()); - } - public function getDashboardLayout($checkToken = true) { if ($checkToken) { diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php index 9ccbb822e4..5a0a7cc0ec 100644 --- a/plugins/Dashboard/Dashboard.php +++ b/plugins/Dashboard/Dashboard.php @@ -11,7 +11,8 @@ namespace Piwik\Plugins\Dashboard; use Piwik\Common; use Piwik\Db; use Piwik\Piwik; -use Piwik\WidgetsList; +use Piwik\Category\Subcategory; +use Piwik\Widget\WidgetConfig; /** */ @@ -26,10 +27,43 @@ class Dashboard extends \Piwik\Plugin 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'UsersManager.deleteUser' => 'deleteDashboardLayout', - 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys' + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', + 'Widget.addWidgetConfigs' => 'addWidgetConfigs', + 'Category.addSubcategories' => 'addSubcategories' ); } + public function addWidgetConfigs(&$widgets) + { + $dashboards = API::getInstance()->getDashboards(); + + foreach ($dashboards as $dashboard) { + $config = new WidgetConfig(); + $config->setIsNotWidgetizable(); + $config->setModule('Dashboard'); + $config->setAction('embeddedIndex'); + $config->setCategoryId('Dashboard_Dashboard'); + $config->setSubcategoryId($dashboard['id']); + $config->setParameters(array('idDashboard' => $dashboard['id'])); + $widgets[] = $config; + } + } + + public function addSubcategories(&$subcategories) + { + $dashboards = API::getInstance()->getDashboards(); + + $order = 0; + foreach ($dashboards as $dashboard) { + $subcategory = new Subcategory(); + $subcategory->setName($dashboard['name']); + $subcategory->setCategoryId('Dashboard_Dashboard'); + $subcategory->setId($dashboard['id']); + $subcategory->setOrder($order++); + $subcategories[] = $subcategory; + } + } + /** * Returns the layout in the DB for the given user, or false if the layout has not been set yet. * Parameters must be checked BEFORE this function call @@ -70,14 +104,14 @@ class Dashboard extends \Piwik\Plugin $defaultLayout = '[ [ - {"uniqueId":"widgetVisitsSummarygetEvolutionGraphcolumnsArray","parameters":{"module":"VisitsSummary","action":"getEvolutionGraph","columns":"nb_visits"}}, + {"uniqueId":"widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution","parameters":{"forceView":"1","viewDataTable":"graphEvolution","module":"VisitsSummary","action":"getEvolutionGraph"}}, {"uniqueId":"widgetLivewidget","parameters":{"module":"Live","action":"widget"}}, - {"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDuration","parameters":{"module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}} + {"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDurationviewDataTablecloud","parameters":{"viewDataTable":"cloud","module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}} ], [ ' . $topWidget . ' {"uniqueId":"widgetReferrersgetWebsites","parameters":{"module":"Referrers","action":"getWebsites"}}, - {"uniqueId":"widgetVisitTimegetVisitInformationPerServerTime","parameters":{"module":"VisitTime","action":"getVisitInformationPerServerTime"}} + {"uniqueId":"widgetVisitTimegetVisitInformationPerServerTimeviewDataTablegraphVerticalBar","parameters":{"viewDataTable": "graphVerticalBar","module":"VisitTime","action":"getVisitInformationPerServerTime"}} ], [ {"uniqueId":"widgetUserCountryMapvisitorMap","parameters":{"module":"UserCountryMap","action":"visitorMap"}}, @@ -160,24 +194,6 @@ class Dashboard extends \Piwik\Plugin ); } - foreach ($layoutObject->columns as &$row) { - if (!is_array($row)) { - $row = array(); - continue; - } - - foreach ($row as $widgetId => $widget) { - if (isset($widget->parameters->module)) { - $controllerName = $widget->parameters->module; - $controllerAction = $widget->parameters->action; - if (!WidgetsList::isDefined($controllerName, $controllerAction)) { - unset($row[$widgetId]); - } - } else { - unset($row[$widgetId]); - } - } - } $layout = $this->encodeLayout($layoutObject); return $layout; } @@ -202,11 +218,13 @@ class Dashboard extends \Piwik\Plugin public function getJsFiles(&$jsFiles) { + $jsFiles[] = "plugins/Dashboard/angularjs/common/services/dashboards-model.js"; $jsFiles[] = "plugins/Dashboard/javascripts/widgetMenu.js"; $jsFiles[] = "libs/javascript/json2.js"; $jsFiles[] = "plugins/Dashboard/javascripts/dashboardObject.js"; $jsFiles[] = "plugins/Dashboard/javascripts/dashboardWidget.js"; $jsFiles[] = "plugins/Dashboard/javascripts/dashboard.js"; + $jsFiles[] = "plugins/Dashboard/angularjs/dashboard/dashboard.directive.js"; } public function getStylesheetFiles(&$stylesheets) diff --git a/plugins/Dashboard/Menu.php b/plugins/Dashboard/Menu.php index f90c117e10..7967635f97 100644 --- a/plugins/Dashboard/Menu.php +++ b/plugins/Dashboard/Menu.php @@ -9,7 +9,6 @@ namespace Piwik\Plugins\Dashboard; use Piwik\Db; -use Piwik\Menu\MenuReporting; use Piwik\Menu\MenuTop; use Piwik\Piwik; use Piwik\Plugins\UsersManager\UserPreferences; @@ -19,24 +18,6 @@ use Piwik\Site; */ class Menu extends \Piwik\Plugin\Menu { - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addItem('Dashboard_Dashboard', '', $this->urlForAction('embeddedIndex', array('idDashboard' => 1)), 5); - - if (!Piwik::isUserIsAnonymous()) { - $login = Piwik::getCurrentUserLogin(); - - $dashboard = new Dashboard(); - $dashboards = $dashboard->getAllDashboards($login); - - $pos = 0; - foreach ($dashboards as $dashboard) { - $menu->addItem('Dashboard_Dashboard', $dashboard['name'], $this->urlForAction('embeddedIndex', array('idDashboard' => $dashboard['iddashboard'])), $pos); - $pos++; - } - } - } - public function configureTopMenu(MenuTop $menu) { $userPreferences = new UserPreferences(); diff --git a/plugins/Dashboard/Model.php b/plugins/Dashboard/Model.php index 790076c1a9..0ef02aeea9 100644 --- a/plugins/Dashboard/Model.php +++ b/plugins/Dashboard/Model.php @@ -10,7 +10,7 @@ namespace Piwik\Plugins\Dashboard; use Piwik\Common; use Piwik\Db; use Piwik\DbHelper; -use Piwik\WidgetsList; +use Piwik\Widget\WidgetsList; class Model { @@ -189,7 +189,11 @@ class Model if ($widget->uniqueId == $oldWidgetId) { - $newWidgetId = WidgetsList::getWidgetUniqueId($newWidget['module'], $newWidget['action'], $newWidget['params']); + if (!empty($newWidget['uniqueId'])) { + $newWidgetId = $newWidget['uniqueId']; + } else { + $newWidgetId = WidgetsList::getWidgetUniqueId($newWidget['module'], $newWidget['action'], $newWidget['params']); + } // is new widget already is on dashboard just remove the old one if (self::layoutContainsWidget($dashboardLayout, $newWidgetId)) { diff --git a/plugins/Dashboard/angularjs/common/services/dashboards-model.js b/plugins/Dashboard/angularjs/common/services/dashboards-model.js new file mode 100644 index 0000000000..e0d861c2ba --- /dev/null +++ b/plugins/Dashboard/angularjs/common/services/dashboards-model.js @@ -0,0 +1,68 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +(function () { + angular.module('piwikApp.service').factory('dashboardsModel', dashboardsModel); + + dashboardsModel.$inject = ['piwikApi']; + + function dashboardsModel (piwikApi) { + + var dashboardsPromise = null; + + var model = { + dashboards: [], + getAllDashboards: getAllDashboards, + reloadAllDashboards: reloadAllDashboards, + getDashboard: getDashboard, + getDashboardLayout: getDashboardLayout + }; + + return model; + + function getDashboard(dashboardId) + { + return getAllDashboards().then(function (dashboards) { + var dashboard = null; + angular.forEach(dashboards, function (board) { + if (parseInt(board.id, 10) === parseInt(dashboardId, 10)) { + dashboard = board; + } + }); + return dashboard; + }); + } + + function getDashboardLayout(dashboardId) + { + return piwikApi.fetch({module: 'Dashboard', action: 'getDashboardLayout', idDashboard: dashboardId}); + } + + function reloadAllDashboards() + { + if (dashboardsPromise) { + dashboardsPromise = null; + } + + return getAllDashboards(); + } + + function getAllDashboards() + { + if (!dashboardsPromise) { + dashboardsPromise = piwikApi.fetch({method: 'Dashboard.getDashboards'}).then(function (response) { + if (response) { + model.dashboards = response; + } + + return response; + }); + } + + return dashboardsPromise; + } + } +})();
\ No newline at end of file diff --git a/plugins/Dashboard/angularjs/dashboard/dashboard.directive.js b/plugins/Dashboard/angularjs/dashboard/dashboard.directive.js new file mode 100644 index 0000000000..578737940c --- /dev/null +++ b/plugins/Dashboard/angularjs/dashboard/dashboard.directive.js @@ -0,0 +1,103 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * <div piwik-dashboard dashboardId="5" layout="10"></div> + */ +(function () { + angular.module('piwikApp').directive('piwikDashboard', piwikDashboard); + + piwikDashboard.$inject = ['dashboardsModel', '$rootScope', '$q']; + + function piwikDashboard(dashboardsModel, $rootScope, $q) { + + function renderDashboard(dashboardId, dashboard, layout) + { + $('.dashboardSettings').show(); + initTopControls(); + + // Embed dashboard + if (!$('#topBars').length) { + $('.dashboardSettings').after($('#Dashboard')); + $('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover'); + } + + widgetsHelper.getAvailableWidgets(); + + $('#dashboardWidgetsArea') + .on('dashboardempty', showEmptyDashboardNotification) + .dashboard({ + idDashboard: dashboardId, + layout: layout, + name: dashboard ? dashboard.name : '' + }); + + $('#columnPreview').find('>div').each(function () { + var width = []; + $('div', this).each(function () { + width.push(this.className.replace(/width-/, '')); + }); + $(this).attr('layout', width.join('-')); + }); + + $('#columnPreview').find('>div').on('click', function () { + $('#columnPreview').find('>div').removeClass('choosen'); + $(this).addClass('choosen'); + }); + } + + function fetchDashboard(dashboardId) { + $('#dashboardWidgetsArea').innerHTML =''; + + var getDashboard = dashboardsModel.getDashboard(dashboardId); + var getLayout = dashboardsModel.getDashboardLayout(dashboardId); + + $q.all([getDashboard, getLayout]).then(function (response) { + var dashboard = response[0]; + var layout = response[1]; + + $(function() { + renderDashboard(dashboardId, dashboard, layout); + }); + }); + } + + function clearDashboard () { + $('.top_controls .dashboard-manager').hide(); + $('#dashboardWidgetsArea').dashboard('destroy'); + } + + return { + restrict: 'A', + scope: { + dashboardid: '=', + layout: '=' + }, + link: function (scope, element, attrs) { + + scope.$parent.fetchDashboard = function (dashboardId) { + scope.dashboardId = dashboardId; + fetchDashboard(dashboardId) + }; + + fetchDashboard(scope.dashboardid); + + function onLocationChange(event, url1, url2) + { + if (url1 !== url2) { + clearDashboard(); + } + } + + // should be rather handled in route or so. + var unbind = $rootScope.$on('$locationChangeSuccess', onLocationChange); + scope.$on('$destroy', onLocationChange); + scope.$on('$destroy', unbind); + } + }; + } +})();
\ No newline at end of file diff --git a/plugins/Dashboard/javascripts/dashboard.js b/plugins/Dashboard/javascripts/dashboard.js index 238aca5197..f78814cdbb 100644 --- a/plugins/Dashboard/javascripts/dashboard.js +++ b/plugins/Dashboard/javascripts/dashboard.js @@ -5,45 +5,12 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -function initDashboard(dashboardId, dashboardLayout) { - - $('.dashboardSettings').show(); - initTopControls(); - - // Embed dashboard - if (!$('#topBars').length) { - $('.dashboardSettings').after($('#Dashboard')); - $('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover'); - } - - widgetsHelper.getAvailableWidgets(); - - $('#dashboardWidgetsArea') - .on('dashboardempty', showEmptyDashboardNotification) - .dashboard({ - idDashboard: dashboardId, - layout: dashboardLayout - }); - - $('#columnPreview').find('>div').each(function () { - var width = []; - $('div', this).each(function () { - width.push(this.className.replace(/width-/, '')); - }); - $(this).attr('layout', width.join('-')); - }); - - $('#columnPreview').find('>div').on('click', function () { - $('#columnPreview').find('>div').removeClass('choosen'); - $(this).addClass('choosen'); - }); -} - function createDashboard() { - $('#createDashboardName').val(''); - piwikHelper.modalConfirm('#createDashboardConfirm', {yes: function () { - var dashboardName = $('#createDashboardName').val(); - var type = ($('#dashboard_type_empty:checked').length > 0) ? 'empty' : 'default'; + $(makeSelectorLastId('createDashboardName')).val(''); + + piwikHelper.modalConfirm(makeSelectorLastId('createDashboardConfirm'), {yes: function () { + var dashboardName = $(makeSelectorLastId('createDashboardName')).val(); + var type = ($('[id=dashboard_type_empty]:last:checked').length > 0) ? 'empty' : 'default'; var ajaxRequest = new ajaxHelper(); ajaxRequest.setLoadingElement(); @@ -57,50 +24,74 @@ function createDashboard() { }, 'post'); ajaxRequest.setCallback( function (id) { - $('#dashboardWidgetsArea').dashboard('loadDashboard', id); + angular.element(document).injector().invoke(function ($location, reportingMenuModel, dashboardsModel) { + dashboardsModel.reloadAllDashboards().then(function () { + $('#dashboardWidgetsArea').dashboard('loadDashboard', id); + $('#dashboardWidgetsArea').dashboard('rebuildMenu'); + }); + }); } ); ajaxRequest.send(true); }}); } +function makeSelectorLastId(domElementId) +{ + // there can be many elements with this id, we prefer the last one + return '[id=' + domElementId + ']:last'; +} + function resetDashboard() { - piwikHelper.modalConfirm('#resetDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); }}); + piwikHelper.modalConfirm(makeSelectorLastId('resetDashboardConfirm'), {yes: + function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); + }}); } function renameDashboard() { - $('#newDashboardName').val($('#dashboardWidgetsArea').dashboard('getDashboardName')); - piwikHelper.modalConfirm('#renameDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('setDashboardName', $('#newDashboardName').val()); }}); + $(makeSelectorLastId('newDashboardName')).val($('#dashboardWidgetsArea').dashboard('getDashboardName')); + + piwikHelper.modalConfirm(makeSelectorLastId('renameDashboardConfirm'), {yes: function () { + var newDashboardName = $(makeSelectorLastId('newDashboardName')).val(); + $('#dashboardWidgetsArea').dashboard('setDashboardName', newDashboardName); + }}); } function removeDashboard() { - $('#removeDashboardConfirm').find('h2 span').text($('#dashboardWidgetsArea').dashboard('getDashboardName')); - piwikHelper.modalConfirm('#removeDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('removeDashboard'); }}); + $(makeSelectorLastId('removeDashboardConfirm')).find('h2 span').text($('#dashboardWidgetsArea').dashboard('getDashboardName')); + + piwikHelper.modalConfirm(makeSelectorLastId('removeDashboardConfirm'), {yes: function () { + $('#dashboardWidgetsArea').dashboard('removeDashboard'); + }}); } function showChangeDashboardLayoutDialog() { $('#columnPreview').find('>div').removeClass('choosen'); $('#columnPreview').find('>div[layout=' + $('#dashboardWidgetsArea').dashboard('getColumnLayout') + ']').addClass('choosen'); - piwikHelper.modalConfirm('#changeDashboardLayout', {yes: function () { - $('#dashboardWidgetsArea').dashboard('setColumnLayout', $('#changeDashboardLayout').find('.choosen').attr('layout')); + + var id = makeSelectorLastId('changeDashboardLayout'); + piwikHelper.modalConfirm(id, {yes: function () { + $('#dashboardWidgetsArea').dashboard('setColumnLayout', $(id).find('.choosen').attr('layout')); }}); } function showEmptyDashboardNotification() { - piwikHelper.modalConfirm('#dashboardEmptyNotification', { + piwikHelper.modalConfirm(makeSelectorLastId('dashboardEmptyNotification'), { resetDashboard: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); }, addWidget: function () { $('.dashboardSettings').trigger('click'); } }); } function setAsDefaultWidgets() { - piwikHelper.modalConfirm('#setAsDefaultWidgetsConfirm', { - yes: function () { $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout'); } + piwikHelper.modalConfirm(makeSelectorLastId('setAsDefaultWidgetsConfirm'), { + yes: function () { + $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout'); + } }); } function copyDashboardToUser() { - $('#copyDashboardName').val($('#dashboardWidgetsArea').dashboard('getDashboardName')); + $(makeSelectorLastId('copyDashboardName')).val($('#dashboardWidgetsArea').dashboard('getDashboardName')); var ajaxRequest = new ajaxHelper(); ajaxRequest.addParams({ module: 'API', @@ -109,13 +100,13 @@ function copyDashboardToUser() { }, 'get'); ajaxRequest.setCallback( function (availableUsers) { - $('#copyDashboardUser').empty(); - $('#copyDashboardUser').append( + $(makeSelectorLastId('copyDashboardUser')).empty(); + $(makeSelectorLastId('copyDashboardUser')).append( $('<option></option>').val(piwik.userLogin).text(piwik.userLogin) ); $.each(availableUsers, function (index, user) { if (user.login != 'anonymous' && user.login != piwik.userLogin) { - $('#copyDashboardUser').append( + $(makeSelectorLastId('copyDashboardUser')).append( $('<option></option>').val(user.login).text(user.login + ' (' + user.alias + ')') ); } @@ -124,10 +115,10 @@ function copyDashboardToUser() { ); ajaxRequest.send(true); - piwikHelper.modalConfirm('#copyDashboardToUserConfirm', { + piwikHelper.modalConfirm(makeSelectorLastId('copyDashboardToUserConfirm'), { yes: function () { - var copyDashboardName = $('#copyDashboardName').val(); - var copyDashboardUser = $('#copyDashboardUser').val(); + var copyDashboardName = $(makeSelectorLastId('copyDashboardName')).val(); + var copyDashboardUser = $(makeSelectorLastId('copyDashboardUser')).val(); var ajaxRequest = new ajaxHelper(); ajaxRequest.addParams({ @@ -291,7 +282,7 @@ function copyDashboardToUser() { }, isWidgetAvailable: function (widgetUniqueId) { - return !$('#dashboardWidgetsArea').find('[widgetId=' + widgetUniqueId + ']').length; + return !$('#dashboardWidgetsArea').find('[widgetId="' + widgetUniqueId + '"]').length; }, widgetSelected: function (widget) { @@ -301,6 +292,7 @@ function copyDashboardToUser() { DashboardManagerControl.initElements = function () { UIControl.initElements(this, '.dashboard-manager'); + $('.top_controls .dashboard-manager').hide(); // initially hide the manager }; exports.DashboardManagerControl = DashboardManagerControl; diff --git a/plugins/Dashboard/javascripts/dashboardObject.js b/plugins/Dashboard/javascripts/dashboardObject.js index 7808d22c11..08bb36dd79 100644 --- a/plugins/Dashboard/javascripts/dashboardObject.js +++ b/plugins/Dashboard/javascripts/dashboardObject.js @@ -57,9 +57,6 @@ if (options.layout) { generateLayout(options.layout); - buildMenu(); - } else { - methods.loadDashboard.apply(this, [dashboardId]); } return this; @@ -89,11 +86,11 @@ dashboardName = ''; dashboardLayout = null; dashboardId = dashboardIdToLoad; - piwikHelper.showAjaxLoading(); - broadcast.updateHashOnly = true; - broadcast.propagateAjax('?idDashboard=' + dashboardIdToLoad); - fetchLayout(generateLayout); - buildMenu(); + + var element = $('[piwik-dashboard]'); + var scope = angular.element(element).scope(); + scope.fetchDashboard(dashboardIdToLoad); + return this; }, @@ -179,6 +176,8 @@ ajaxRequest.send(true); }, + rebuildMenu: rebuildMenu, + /** * Removes the current dashboard */ @@ -201,6 +200,39 @@ } }; + function removeNonExistingWidgets(availableWidgets, layout) + { + var existingModuleAction = {}; + $.each(availableWidgets, function (category, widgets) { + $.each(widgets, function (index, widget) { + existingModuleAction[widget.module + '.' + widget.action] = true; + }); + }); + + var columns = []; + $.each(layout.columns, function (i, column) { + var widgets = []; + + $.each(column, function (j, widget) { + if (!widget.parameters || !widget.parameters.module) { + return; + } + + var method = widget.parameters.module + '.' + widget.parameters.action + if (existingModuleAction[method]) { + widgets.push(widget); + } + + }); + + columns[i] = widgets; + }); + + layout.columns = columns; + + return layout; + } + /** * Generates the dashboard out of the given layout * @@ -209,46 +241,33 @@ function generateLayout(layout) { dashboardLayout = parseLayout(layout); - piwikHelper.hideAjaxLoading(); - adjustDashboardColumns(dashboardLayout.config.layout); - - var dashboardContainsWidgets = false; - for (var column = 0; column < dashboardLayout.columns.length; column++) { - for (var i in dashboardLayout.columns[column]) { - if (typeof dashboardLayout.columns[column][i] != 'object') { - // Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function. - // If we would continue with i="indexOf", an invalid widget would be created. - continue; + + widgetsHelper.getAvailableWidgets(function (availableWidgets) { + dashboardLayout = removeNonExistingWidgets(availableWidgets, dashboardLayout); + + piwikHelper.hideAjaxLoading(); + adjustDashboardColumns(dashboardLayout.config.layout); + + var dashboardContainsWidgets = false; + for (var column = 0; column < dashboardLayout.columns.length; column++) { + for (var i in dashboardLayout.columns[column]) { + if (typeof dashboardLayout.columns[column][i] != 'object') { + // Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function. + // If we would continue with i="indexOf", an invalid widget would be created. + continue; + } + var widget = dashboardLayout.columns[column][i]; + dashboardContainsWidgets = true; + addWidgetTemplate(widget.uniqueId, column + 1, widget.parameters, false, widget.isHidden) } - var widget = dashboardLayout.columns[column][i]; - dashboardContainsWidgets = true; - addWidgetTemplate(widget.uniqueId, column + 1, widget.parameters, false, widget.isHidden) } - } - - if (!dashboardContainsWidgets) { - $(dashboardElement).trigger('dashboardempty'); - } - makeWidgetsSortable(); - } + if (!dashboardContainsWidgets) { + $(dashboardElement).trigger('dashboardempty'); + } - /** - * Fetches the layout for the currently set dashboard id - * and passes the response to given callback function - * - * @param {function} callback - */ - function fetchLayout(callback) { - globalAjaxQueue.abort(); - var ajaxRequest = new ajaxHelper(); - ajaxRequest.addParams({ - module: 'Dashboard', - action: 'getDashboardLayout', - idDashboard: dashboardId - }, 'get'); - ajaxRequest.setCallback(callback); - ajaxRequest.send(false); + makeWidgetsSortable(); + }); } /** @@ -388,7 +407,7 @@ * @param {String} uniqueId */ function reloadWidget(uniqueId) { - $('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget('reload', false, true); + $('[widgetId="' + uniqueId + '"]', dashboardElement).dashboardWidget('reload', false, true); } /** @@ -417,7 +436,7 @@ $('.col:nth-child(' + columnNumber + ')', dashboardElement).append(widgetContent); } - $('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget({ + $('[widgetId="' + uniqueId + '"]', dashboardElement).dashboardWidget({ uniqueId: uniqueId, widgetParameters: widgetParameters, onChange: function () { @@ -468,7 +487,19 @@ /** * Handle clicks for menu items for choosing between available dashboards */ - function buildMenu() { + function rebuildMenu() { + + if ($('[piwik-reporting-menu]').length) { + // dashboard in reporting page (regular Piwik UI) + angular.element(document).injector().invoke(function (reportingMenuModel) { + reportingMenuModel.reloadMenuItems(); + }); + return; + } + + var _self = this; + + // widgetized var success = function (dashboards) { var dashboardMenuList = $('#Dashboard').find('> ul'); var dashboardMenuListItems = dashboardMenuList.find('>li'); @@ -484,7 +515,7 @@ for (var i = 0; i < dashboards.length; i++) { var $link = $('<a/>').attr('data-idDashboard', dashboards[i].iddashboard).text(dashboards[i].name); var $li = $('<li/>').attr('id', 'Dashboard_embeddedIndex_' + dashboards[i].iddashboard) - .addClass('dashboardMenuItem').append($link); + .addClass('dashboardMenuItem').append($link); items.push($li); if (dashboards[i].iddashboard == dashboardId) { @@ -502,15 +533,10 @@ var idDashboard = $(this).attr('data-idDashboard'); - if (typeof piwikMenu != 'undefined') { - piwikMenu.activateMenu('Dashboard', 'embeddedIndex'); - } $('#Dashboard ul li').removeClass('sfHover'); - if ($(dashboardElement).length) { - $(dashboardElement).dashboard('loadDashboard', idDashboard); - } else { - broadcast.propagateAjax('module=Dashboard&action=embeddedIndex&idDashboard=' + idDashboard); - } + + methods.loadDashboard.apply(_self, [idDashboard]); + $(this).closest('li').addClass('sfHover'); }); }; @@ -521,7 +547,7 @@ action: 'getAllDashboards' }, 'get'); ajaxRequest.setCallback(success); - ajaxRequest.send(false); + ajaxRequest.send(); } /** @@ -568,7 +594,7 @@ function () { if (dashboardChanged) { dashboardChanged = false; - buildMenu(); + rebuildMenu(); } } ); @@ -594,6 +620,7 @@ }, 'get'); ajaxRequest.setCallback( function () { + rebuildMenu(); methods.loadDashboard.apply(this, [1]); } ); diff --git a/plugins/Dashboard/javascripts/dashboardWidget.js b/plugins/Dashboard/javascripts/dashboardWidget.js index accd0aa2c2..969ed9db28 100755 --- a/plugins/Dashboard/javascripts/dashboardWidget.js +++ b/plugins/Dashboard/javascripts/dashboardWidget.js @@ -70,7 +70,7 @@ */ destroy: function () { if (this.isMaximised) { - $('[widgetId=' + this.uniqueId + ']').dialog('destroy'); + $('[widgetId="' + this.uniqueId + '"]').dialog('destroy'); } $('*', this.element).off('.dashboardWidget'); // unbind all events $('.widgetContent', this.element).trigger('widget:destroy'); @@ -197,7 +197,7 @@ var emptyWidgetContent = require('piwik/UI/Dashboard').WidgetFactory.make(uniqueId, title); this.element.html(emptyWidgetContent); - var widgetElement = $('#' + uniqueId, this.element); + var widgetElement = $('[id="' + uniqueId + '"]', this.element); var self = this; widgetElement .on('mouseenter.dashboardWidget', function () { @@ -312,7 +312,7 @@ } $('body').off('.dashboardWidget'); $(this).dialog("destroy"); - $('#' + self.uniqueId + '-placeholder').replaceWith(this); + $('[id="' + self.uniqueId + '-placeholder"]').replaceWith(this); $(this).removeAttr('style'); self.options.onChange(); $(this).find('div.piwik-graph').trigger('resizeGraph'); @@ -339,6 +339,9 @@ */ detachWidget: function () { this.element.before('<div id="' + this.uniqueId + '-placeholder" class="widgetPlaceholder widget"> </div>'); + var placeholder = $('[id="' + self.uniqueId + '-placeholder"]') + + $('#' + this.uniqueId + '-placeholder').height(this.element.height()); $('#' + this.uniqueId + '-placeholder').width(this.element.width() - 16); diff --git a/plugins/Dashboard/javascripts/widgetMenu.js b/plugins/Dashboard/javascripts/widgetMenu.js index 6d58415cb9..169768eb03 100644 --- a/plugins/Dashboard/javascripts/widgetMenu.js +++ b/plugins/Dashboard/javascripts/widgetMenu.js @@ -13,21 +13,82 @@ function widgetsHelper() { * * @return {object} object containing available widgets */ -widgetsHelper.getAvailableWidgets = function () { +widgetsHelper.getAvailableWidgets = function (callback) { + + function mergeCategoriesAndSubCategories(availableWidgets) + { + var categorized = {}; + + $.each(availableWidgets, function (index, widget) { + var category = widget.category.name; + + if (!categorized[category]) { + categorized[category] = {'-': []}; + } + + var subcategory = '-'; + if (widget.subcategory && widget.subcategory.name) { + subcategory = widget.subcategory.name; + } + + if (!categorized[category][subcategory]) { + categorized[category][subcategory] = []; + } + + categorized[category][subcategory].push(widget); + }); + + var moved = {}; + + $.each(categorized, function (category, widgets) { + $.each(widgets, function (subcategory, subwidgets) { + + var categoryToUse = category; + if (subwidgets.length >= 3 && subcategory !== '-') { + categoryToUse = category + ' - ' + subcategory; + } + + if (!moved[categoryToUse]) { + moved[categoryToUse] = []; + } + + $.each(subwidgets, function (index, widget) { + moved[categoryToUse].push(widget); + }); + }); + }); + + return moved; + } + if (!widgetsHelper.availableWidgets) { var ajaxRequest = new ajaxHelper(); + ajaxRequest._mixinDefaultGetParams = function (params) { + return params; + }; ajaxRequest.addParams({ - module: 'Dashboard', - action: 'getAvailableWidgets' + module: 'API', + method: 'API.getWidgetMetadata', + format: 'JSON', + deep: '1', + idSite: piwik.idSite || broadcast.getValueFromUrl('idSite') }, 'get'); ajaxRequest.setCallback( function (data) { - widgetsHelper.availableWidgets = data; + widgetsHelper.availableWidgets = mergeCategoriesAndSubCategories(data); + + if (callback) { + callback(widgetsHelper.availableWidgets); + } } ); ajaxRequest.send(true); } + if (callback) { + callback(widgetsHelper.availableWidgets); + } + return widgetsHelper.availableWidgets; }; @@ -191,6 +252,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid * @return {$} category list element */ function createWidgetCategoryList(widgetPreview, availableWidgets) { + var settings = widgetPreview.settings; if (!$('.' + settings.categorylistClass, widgetPreview).length) { @@ -200,7 +262,6 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid } for (var widgetCategory in availableWidgets) { - $('.' + settings.categorylistClass, widgetPreview).append('<li>' + widgetCategory + '</li>'); } @@ -257,6 +318,8 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid widgetClass += ' ' + settings.unavailableClass; } + widgetName = piwikHelper.escape(piwikHelper.htmlEntities(widgetName)); + widgetList.append('<li class="' + widgetClass + '" uniqueid="' + widgetUniqueId + '">' + widgetName + '</li>'); } @@ -278,7 +341,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid clearTimeout(widgetPreview); }); - $('li:not(.' + settings.unavailableClass + ')', widgetList).on('click', function () { + $('li', widgetList).on('click', function () { if (!$('.widgetLoading', widgetPreview).length) { settings.onSelect($(this).attr('uniqueid')); if (settings.resetOnSelect) { @@ -317,7 +380,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid */ function showPreview(widgetUniqueId, widgetPreview) { // do not reload id widget already displayed - if ($('#' + widgetUniqueId, widgetPreview).length) return; + if ($('[id="' + widgetUniqueId + '"]', widgetPreview).length) return; var settings = widgetPreview.settings; @@ -335,7 +398,8 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid previewElement.html(emptyWidgetHtml); var onWidgetLoadedCallback = function (response) { - var widgetElement = $('#' + widgetUniqueId); + var widgetElement = $(document.getElementById(widgetUniqueId)); + // document.getElementById needed for widgets with uniqueid like widgetOpens+Contact+Form $('.widgetContent', widgetElement).html($(response)); $('.widgetContent', widgetElement).trigger('widget:create'); settings.onPreviewLoaded(widgetUniqueId, widgetElement); @@ -398,19 +462,21 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid this.onPreviewLoaded = this.settings.onPreviewLoaded; } - availableWidgets = widgetsHelper.getAvailableWidgets(); + var self = this; + widgetsHelper.getAvailableWidgets(function (availableWidgets) { - var categoryList = createWidgetCategoryList(this, availableWidgets); + var categoryList = createWidgetCategoryList(self, availableWidgets); - var self = this; - $('li', categoryList).on('mouseover', function () { - var category = $(this).text(); - var widgets = availableWidgets[category]; - $('li', categoryList).removeClass(self.settings.choosenClass); - $(this).addClass(self.settings.choosenClass); - showWidgetList(widgets, self); - createPreviewElement(self); // empty preview + $('li', categoryList).on('mouseover', function () { + var category = $(this).text(); + var widgets = availableWidgets[category]; + $('li', categoryList).removeClass(self.settings.choosenClass); + $(this).addClass(self.settings.choosenClass); + showWidgetList(widgets, self); + createPreviewElement(self); // empty preview + }); }); + }; } }); diff --git a/plugins/Dashboard/templates/embeddedIndex.twig b/plugins/Dashboard/templates/embeddedIndex.twig index af1dbeb887..a7633ca193 100644 --- a/plugins/Dashboard/templates/embeddedIndex.twig +++ b/plugins/Dashboard/templates/embeddedIndex.twig @@ -1,10 +1,4 @@ -<script type="text/javascript"> - widgetsHelper.availableWidgets = {{ availableWidgets|raw }}; - $(function() { - initDashboard({{ dashboardId }}, {{ dashboardLayout|raw }}); - }); -</script> -<div id="dashboard"> +<div id="dashboard" piwik-dashboard dashboardid="{{ dashboardId }}"> <div class="ui-confirm" id="confirm"> <h2>{{ 'Dashboard_DeleteWidgetConfirm'|translate }}</h2> <input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/> diff --git a/plugins/DevicePlugins/Reports/Base.php b/plugins/DevicePlugins/Reports/Base.php index 81116f4d1d..b6f8755b13 100644 --- a/plugins/DevicePlugins/Reports/Base.php +++ b/plugins/DevicePlugins/Reports/Base.php @@ -15,7 +15,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_VisitorSettings'; + $this->categoryId = 'General_Visitors'; } protected function getBasicDevicePluginsDisplayProperties(ViewDataTable $view) diff --git a/plugins/DevicePlugins/Reports/GetPlugin.php b/plugins/DevicePlugins/Reports/GetPlugin.php index 151bf3e1a6..42fef1cb89 100644 --- a/plugins/DevicePlugins/Reports/GetPlugin.php +++ b/plugins/DevicePlugins/Reports/GetPlugin.php @@ -23,8 +23,9 @@ class GetPlugin extends Base $this->metrics = array('nb_visits'); $this->constantRowsCount = true; $this->processedMetrics = array('nb_visits_percentage'); - $this->order = 4; - $this->widgetTitle = 'DevicePlugins_WidgetPlugins'; + $this->order = 13; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function configureView(ViewDataTable $view) diff --git a/plugins/DevicesDetection/Controller.php b/plugins/DevicesDetection/Controller.php index 10c55a8a96..d19d0cf403 100644 --- a/plugins/DevicesDetection/Controller.php +++ b/plugins/DevicesDetection/Controller.php @@ -13,52 +13,11 @@ use Piwik\Common; use Piwik\Db; use Piwik\Piwik; use Piwik\Plugin\ControllerAdmin; -use Piwik\Plugin\Manager AS PluginManager; use Piwik\Plugin\Report; use Piwik\View; class Controller extends \Piwik\Plugin\Controller { - public function index() - { - return $this->devices(); - } - - public function devices() - { - $view = new View('@DevicesDetection/devices'); - $view->deviceTypes = $this->renderReport('getType'); - $view->deviceBrands = $this->renderReport('getBrand'); - $view->deviceModels = $this->renderReport('getModel'); - - $isResolutionEnabled = PluginManager::getInstance()->isPluginActivated('Resolution'); - if ($isResolutionEnabled) { - $view->resolutions = $this->renderReport(Report::factory('Resolution', 'getResolution')); - } - - return $view->render(); - } - - public function software() - { - $view = new View('@DevicesDetection/software'); - $view->osReport = $this->renderReport('getOsVersions'); - $view->browserReport = $this->renderReport('getBrowsers'); - $view->browserEngineReport = $this->renderReport('getBrowserEngines'); - - $isResolutionEnabled = PluginManager::getInstance()->isPluginActivated('Resolution'); - if ($isResolutionEnabled) { - $view->configurations = $this->renderReport(Report::factory('Resolution', 'getConfiguration')); - } - - $isDevicePluginsEnabled = PluginManager::getInstance()->isPluginActivated('DevicePlugins'); - if ($isDevicePluginsEnabled) { - $view->browserPlugins = $this->renderReport(Report::factory('DevicePlugins', 'getPlugin')); - } - - return $view->render(); - } - public function detection() { Piwik::checkUserHasSomeAdminAccess(); diff --git a/plugins/DevicesDetection/Menu.php b/plugins/DevicesDetection/Menu.php index 7ddb1dc668..067f8b9afa 100644 --- a/plugins/DevicesDetection/Menu.php +++ b/plugins/DevicesDetection/Menu.php @@ -9,7 +9,6 @@ namespace Piwik\Plugins\DevicesDetection; use Piwik\Menu\MenuAdmin; -use Piwik\Menu\MenuReporting; use Piwik\Piwik; /** @@ -24,10 +23,4 @@ class Menu extends \Piwik\Plugin\Menu $order = 40); } } - - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addVisitorsItem('DevicesDetection_Devices', $this->urlForAction('devices')); - $menu->addVisitorsItem('DevicesDetection_Software', $this->urlForAction('software')); - } } diff --git a/plugins/DevicesDetection/Reports/Base.php b/plugins/DevicesDetection/Reports/Base.php index 15eac96a4a..cb24a376e4 100644 --- a/plugins/DevicesDetection/Reports/Base.php +++ b/plugins/DevicesDetection/Reports/Base.php @@ -14,6 +14,6 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'DevicesDetection_DevicesDetection'; + $this->categoryId = 'General_Visitors'; } } diff --git a/plugins/DevicesDetection/Reports/GetBrand.php b/plugins/DevicesDetection/Reports/GetBrand.php index 070b2d8918..41b9fd5366 100644 --- a/plugins/DevicesDetection/Reports/GetBrand.php +++ b/plugins/DevicesDetection/Reports/GetBrand.php @@ -20,8 +20,9 @@ class GetBrand extends Base $this->dimension = new DeviceBrand(); $this->name = Piwik::translate('DevicesDetection_DeviceBrand'); $this->documentation = ''; // TODO - $this->order = 1; - $this->widgetTitle = 'DevicesDetection_DeviceBrand'; + $this->order = 4; + + $this->subcategoryId = 'DevicesDetection_Devices'; } public function configureView(ViewDataTable $view) diff --git a/plugins/DevicesDetection/Reports/GetBrowserEngines.php b/plugins/DevicesDetection/Reports/GetBrowserEngines.php index 7c47ffca63..403955c66c 100644 --- a/plugins/DevicesDetection/Reports/GetBrowserEngines.php +++ b/plugins/DevicesDetection/Reports/GetBrowserEngines.php @@ -21,8 +21,9 @@ class GetBrowserEngines extends Base $this->dimension = new BrowserEngine(); $this->name = Piwik::translate('DevicesDetection_BrowserEngines'); $this->documentation = Piwik::translate('DevicesDetection_BrowserEngineDocumentation', '<br />'); - $this->order = 7; - $this->widgetTitle = 'DevicesDetection_BrowserEngines'; + $this->order = 10; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function getDefaultTypeViewDataTable() diff --git a/plugins/DevicesDetection/Reports/GetBrowserVersions.php b/plugins/DevicesDetection/Reports/GetBrowserVersions.php index 4f5875199a..66c7f91f55 100644 --- a/plugins/DevicesDetection/Reports/GetBrowserVersions.php +++ b/plugins/DevicesDetection/Reports/GetBrowserVersions.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\BrowserVersion; +use Piwik\Plugin\Reports; class GetBrowserVersions extends Base { @@ -20,8 +21,9 @@ class GetBrowserVersions extends Base $this->dimension = new BrowserVersion(); $this->name = Piwik::translate('DevicesDetection_BrowserVersion'); $this->documentation = ''; // TODO - $this->order = 2; - $this->widgetTitle = 'DevicesDetection_BrowserVersion'; + $this->order = 6; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function configureView(ViewDataTable $view) @@ -34,7 +36,7 @@ class GetBrowserVersions extends Base public function getRelatedReports() { return array( - self::factory('DevicesDetection', 'getBrowsers'), + Reports::factory('DevicesDetection', 'getBrowsers'), ); } } diff --git a/plugins/DevicesDetection/Reports/GetBrowsers.php b/plugins/DevicesDetection/Reports/GetBrowsers.php index 7d08a51ce5..5c39a275ee 100644 --- a/plugins/DevicesDetection/Reports/GetBrowsers.php +++ b/plugins/DevicesDetection/Reports/GetBrowsers.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\BrowserName; +use Piwik\Plugin\Reports; class GetBrowsers extends Base { @@ -18,10 +19,11 @@ class GetBrowsers extends Base { parent::init(); $this->dimension = new BrowserName(); - $this->name = Piwik::translate('DevicesDetection_WidgetBrowsers'); + $this->name = Piwik::translate('DevicesDetection_Browsers'); $this->documentation = Piwik::translate('DevicesDetection_WidgetBrowsersDocumentation', '<br />'); - $this->order = 1; - $this->widgetTitle = 'DevicesDetection_WidgetBrowsers'; + $this->order = 5; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function configureView(ViewDataTable $view) @@ -35,7 +37,7 @@ class GetBrowsers extends Base public function getRelatedReports() { return array( - self::factory('DevicesDetection', 'getBrowserVersions'), + Reports::factory('DevicesDetection', 'getBrowserVersions'), ); } } diff --git a/plugins/DevicesDetection/Reports/GetModel.php b/plugins/DevicesDetection/Reports/GetModel.php index e2241e1ad4..154464691c 100644 --- a/plugins/DevicesDetection/Reports/GetModel.php +++ b/plugins/DevicesDetection/Reports/GetModel.php @@ -21,7 +21,8 @@ class GetModel extends Base $this->name = Piwik::translate('DevicesDetection_DeviceModel'); $this->documentation = ''; // TODO $this->order = 2; - $this->widgetTitle = 'DevicesDetection_DeviceModel'; + + $this->subcategoryId = 'DevicesDetection_Devices'; } public function configureView(ViewDataTable $view) diff --git a/plugins/DevicesDetection/Reports/GetOsFamilies.php b/plugins/DevicesDetection/Reports/GetOsFamilies.php index 61b393cb44..a283a83f7b 100644 --- a/plugins/DevicesDetection/Reports/GetOsFamilies.php +++ b/plugins/DevicesDetection/Reports/GetOsFamilies.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\Os; +use Piwik\Plugin\Reports; class GetOsFamilies extends Base { @@ -20,8 +21,9 @@ class GetOsFamilies extends Base $this->dimension = new Os(); $this->name = Piwik::translate('DevicesDetection_OperatingSystemFamilies'); $this->documentation = ''; // TODO - $this->order = 3; - $this->widgetTitle = 'DevicesDetection_OperatingSystemFamilies'; + $this->order = 8; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function configureView(ViewDataTable $view) @@ -35,7 +37,7 @@ class GetOsFamilies extends Base public function getRelatedReports() { return array( - self::factory('DevicesDetection', 'getOsVersions'), + Reports::factory('DevicesDetection', 'getOsVersions'), ); } diff --git a/plugins/DevicesDetection/Reports/GetOsVersions.php b/plugins/DevicesDetection/Reports/GetOsVersions.php index 8676f38776..b330054c6f 100644 --- a/plugins/DevicesDetection/Reports/GetOsVersions.php +++ b/plugins/DevicesDetection/Reports/GetOsVersions.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\DevicesDetection\Columns\OsVersion; +use Piwik\Plugin\Reports; class GetOsVersions extends Base { @@ -20,8 +21,9 @@ class GetOsVersions extends Base $this->dimension = new OsVersion(); $this->name = Piwik::translate('DevicesDetection_OperatingSystemVersions'); $this->documentation = ''; // TODO - $this->order = 4; - $this->widgetTitle = 'DevicesDetection_OperatingSystemVersions'; + $this->order = 2; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function configureView(ViewDataTable $view) @@ -35,7 +37,7 @@ class GetOsVersions extends Base public function getRelatedReports() { return array( - self::factory('DevicesDetection', 'getOsFamilies'), + Reports::factory('DevicesDetection', 'getOsFamilies'), ); } } diff --git a/plugins/DevicesDetection/Reports/GetType.php b/plugins/DevicesDetection/Reports/GetType.php index cbfa69db3d..b12f19f319 100644 --- a/plugins/DevicesDetection/Reports/GetType.php +++ b/plugins/DevicesDetection/Reports/GetType.php @@ -22,7 +22,8 @@ class GetType extends Base $this->name = Piwik::translate('DevicesDetection_DeviceType'); $this->documentation = ''; // TODO $this->order = 0; - $this->widgetTitle = 'DevicesDetection_DeviceType'; + + $this->subcategoryId = 'DevicesDetection_Devices'; } public function configureView(ViewDataTable $view) diff --git a/plugins/DevicesDetection/templates/devices.twig b/plugins/DevicesDetection/templates/devices.twig deleted file mode 100644 index a37079c9fd..0000000000 --- a/plugins/DevicesDetection/templates/devices.twig +++ /dev/null @@ -1,19 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ "DevicesDetection_DeviceType"|translate }}</h2> - {{ deviceTypes | raw}} - <h2 piwik-enriched-headline>{{ "DevicesDetection_DeviceBrand"|translate }}</h2> - {{ deviceBrands | raw }} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ "DevicesDetection_DeviceModel"|translate }}</h2> - {{ deviceModels | raw }} - {% if resolutions|default is not empty %} - <h2 piwik-enriched-headline>{{ 'Resolution_Resolutions'|translate }}</h2> - {{ resolutions|raw }} - {% endif %} - </div> - -</div> diff --git a/plugins/DevicesDetection/templates/software.twig b/plugins/DevicesDetection/templates/software.twig deleted file mode 100644 index 8b0dab0631..0000000000 --- a/plugins/DevicesDetection/templates/software.twig +++ /dev/null @@ -1,23 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ "DevicesDetection_OperatingSystems"|translate }}</h2> - {{ osReport | raw}} - {% if configurations|default is not empty %} - <h2 piwik-enriched-headline>{{ 'Resolution_Configurations'|translate }}</h2> - {{ configurations|raw }} - {% endif %} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ "DevicesDetection_Browsers"|translate }}</h2> - {{ browserReport | raw }} - <h2 piwik-enriched-headline>{{ "DevicesDetection_BrowserEngines"|translate }}</h2> - {{ browserEngineReport | raw }} - {% if browserPlugins|default is not empty %} - <h2 piwik-enriched-headline>{{ 'General_Plugins'|translate }}</h2> - {{ browserPlugins|raw }} - {% endif %} - </div> - -</div> diff --git a/plugins/Ecommerce/Categories/EcommerceCategory.php b/plugins/Ecommerce/Categories/EcommerceCategory.php new file mode 100644 index 0000000000..39cd5bb3ea --- /dev/null +++ b/plugins/Ecommerce/Categories/EcommerceCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Categories; + +use Piwik\Category\Category; + +class EcommerceCategory extends Category +{ + protected $id = 'Goals_Ecommerce'; + protected $order = 20; +} diff --git a/plugins/Ecommerce/Categories/EcommerceLogSubcategory.php b/plugins/Ecommerce/Categories/EcommerceLogSubcategory.php new file mode 100644 index 0000000000..f3999fda02 --- /dev/null +++ b/plugins/Ecommerce/Categories/EcommerceLogSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Categories; + +use Piwik\Category\Subcategory; + +class EcommerceLogSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Ecommerce'; + protected $id = 'Goals_EcommerceLog'; + protected $order = 5; + +} diff --git a/plugins/Ecommerce/Categories/EcommerceOverviewSubcategory.php b/plugins/Ecommerce/Categories/EcommerceOverviewSubcategory.php new file mode 100644 index 0000000000..b8560a8072 --- /dev/null +++ b/plugins/Ecommerce/Categories/EcommerceOverviewSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Categories; + +use Piwik\Category\Subcategory; + +class EcommerceOverviewSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Ecommerce'; + protected $id = 'General_Overview'; + protected $order = 2; + +} diff --git a/plugins/Ecommerce/Categories/ProductSubcategory.php b/plugins/Ecommerce/Categories/ProductSubcategory.php new file mode 100644 index 0000000000..d7d8e9a810 --- /dev/null +++ b/plugins/Ecommerce/Categories/ProductSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Categories; + +use Piwik\Category\Subcategory; + +class ProductSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Ecommerce'; + protected $id = 'Goals_Products'; + protected $order = 10; + +} diff --git a/plugins/Ecommerce/Categories/SalesSubcategory.php b/plugins/Ecommerce/Categories/SalesSubcategory.php new file mode 100644 index 0000000000..0cb68d3d1c --- /dev/null +++ b/plugins/Ecommerce/Categories/SalesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Categories; + +use Piwik\Category\Subcategory; + +class SalesSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Ecommerce'; + protected $id = 'Ecommerce_Sales'; + protected $order = 15; + +} diff --git a/plugins/Ecommerce/Controller.php b/plugins/Ecommerce/Controller.php index 7cfb955184..780c905c7c 100644 --- a/plugins/Ecommerce/Controller.php +++ b/plugins/Ecommerce/Controller.php @@ -8,7 +8,8 @@ */ namespace Piwik\Plugins\Ecommerce; -use Exception; +use Piwik\API\Request; +use Piwik\Common; use Piwik\DataTable; use Piwik\FrontController; use Piwik\Piwik; @@ -30,95 +31,75 @@ class Controller extends \Piwik\Plugins\Goals\Controller parent::__construct($translator, $translationHelper); } - public function ecommerceReport() + public function getSparklines() { - if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('CustomVariables')) { - throw new Exception("Ecommerce Tracking requires that the plugin Custom Variables is enabled. Please enable the plugin CustomVariables (or ask your admin)."); - } + $idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER; - $view = $this->getGoalReportView($idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER); - $view->displayFullReport = false; - $view->headline = $this->translator->translate('General_EvolutionOverPeriod'); + $view = new View('@Ecommerce/getSparklines'); + $view->onlyConversionOverview = false; + $view->conversionsOverViewEnabled = true; - return $view->render(); - } + if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { + $goalDefinition['name'] = $this->translator->translate('Goals_Ecommerce'); + $goalDefinition['allow_multiple'] = true; + } else { + if (!isset($this->goals[$idGoal])) { + Piwik::redirectToModule('Goals', 'index', array('idGoal' => null)); + } + $goalDefinition = $this->goals[$idGoal]; + } - public function ecommerceLogReport($fetch = false) - { - $view = new View('@Ecommerce/ecommerceLog'); $this->setGeneralVariablesView($view); - $view->ecommerceLog = $this->getEcommerceLog($fetch); - - return $view->render(); - } + $goal = $this->getMetricsForGoal($idGoal); + foreach ($goal as $name => $value) { + $view->$name = $value; + } - public function getEcommerceLog($fetch = false) - { - $saveGET = $_GET; - $_GET['segment'] = urlencode('visitEcommerceStatus!=none'); - $_GET['widget'] = 1; - $output = FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch)); - $_GET = $saveGET; + if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { + $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART); + foreach ($goal as $name => $value) { + $name = 'cart_' . $name; + $view->$name = $value; + } + } - return $output; - } + $view->idGoal = $idGoal; + $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple']; - public function index() - { - return $this->ecommerceReport(); + return $view->render(); } - public function products() + public function getConversionsOverview() { - $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER); - $conversions = 0; - if (!empty($goal['nb_conversions'])) { - $conversions = $goal['nb_conversions']; - } - - $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART); - - $cartNbConversions = 0; - if (!empty($goal) && array_key_exists('nb_conversions', $goal)) { - $cartNbConversions = $goal['nb_conversions']; - } + $view = new View('@Ecommerce/conversionOverview'); + $idGoal = Common::getRequestVar('idGoal', null, 'string'); - $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0; + $goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal)); + $dataRow = $goalMetrics->getFirstRow(); - $goalReportsByDimension = new View\ReportsByDimension('Goals'); + $view->idSite = Common::getRequestVar('idSite', null, 'int'); - $ecommerceCustomParams = array(); - if ($preloadAbandonedCart) { - $ecommerceCustomParams['abandonedCarts'] = '1'; - } else { - $ecommerceCustomParams['abandonedCarts'] = '0'; + if ($dataRow) { + $view->revenue = $dataRow->getColumn('revenue'); + $view->revenue_subtotal = $dataRow->getColumn('revenue_subtotal'); + $view->revenue_tax = $dataRow->getColumn('revenue_tax'); + $view->revenue_shipping = $dataRow->getColumn('revenue_shipping'); + $view->revenue_discount = $dataRow->getColumn('revenue_discount'); } - $goalReportsByDimension->addReport( - 'Goals_Products', 'Goals_ProductSKU', 'Goals.getItemsSku', $ecommerceCustomParams); - $goalReportsByDimension->addReport( - 'Goals_Products', 'Goals_ProductName', 'Goals.getItemsName', $ecommerceCustomParams); - $goalReportsByDimension->addReport( - 'Goals_Products', 'Goals_ProductCategory', 'Goals.getItemsCategory', $ecommerceCustomParams); - - $view = new View('@Ecommerce/products'); - $this->setGeneralVariablesView($view); - - $view->productsByDimension = $goalReportsByDimension->render(); return $view->render(); } - public function sales() + public function getEcommerceLog($fetch = false) { - $viewOverview = $this->getGoalReportView(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER); - $reportsByDimension = $viewOverview->goalReportsByDimension; - - $view = new View('@Ecommerce/sales'); - $this->setGeneralVariablesView($view); + $saveGET = $_GET; + $_GET['segment'] = urlencode('visitEcommerceStatus!=none'); + $_GET['widget'] = 1; + $output = FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch)); + $_GET = $saveGET; - $view->goalReportsByDimension = $reportsByDimension; - return $view->render(); + return $output; } } diff --git a/plugins/Ecommerce/Menu.php b/plugins/Ecommerce/Menu.php deleted file mode 100644 index 331715be4f..0000000000 --- a/plugins/Ecommerce/Menu.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Ecommerce; - -use Piwik\Common; -use Piwik\Menu\MenuReporting; -use Piwik\Menu\MenuUser; -use Piwik\Piwik; -use Piwik\Site; -use Piwik\Url; - -/** - */ -class Menu extends \Piwik\Plugin\Menu -{ - - public function configureReportingMenu(MenuReporting $menu) - { - $idSite = Common::getRequestVar('idSite', null, 'int'); - - $site = new Site($idSite); - - if ($site->isEcommerceEnabled()) { - $ecommerceParams = array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER); - $ecommerceUrl = $this->urlForAction('ecommerceReport', $ecommerceParams); - - $menu->addItem('Goals_Ecommerce', '', $ecommerceUrl, 24); - $menu->addItem('Goals_Ecommerce', 'General_Overview', $ecommerceUrl, 1); - $menu->addItem('Goals_Ecommerce', 'Goals_EcommerceLog', $this->urlForAction('ecommerceLogReport'), 2); - $menu->addItem('Goals_Ecommerce', 'Goals_Products', $this->urlForAction('products', $ecommerceParams), 3); - $menu->addItem('Goals_Ecommerce', 'Ecommerce_Sales', $this->urlForAction('sales', $ecommerceParams), 4); - } - - } -} diff --git a/plugins/Ecommerce/Reports/Base.php b/plugins/Ecommerce/Reports/Base.php index 08a766792a..28a3a4a36f 100644 --- a/plugins/Ecommerce/Reports/Base.php +++ b/plugins/Ecommerce/Reports/Base.php @@ -8,20 +8,17 @@ */ namespace Piwik\Plugins\Ecommerce\Reports; -use Piwik\API\Proxy; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Site; -use Piwik\ViewDataTable\Factory as ViewDataTableFactory; -use Piwik\WidgetsList; abstract class Base extends Report { protected function init() { $this->module = 'Goals'; - $this->category = 'Goals_Ecommerce'; + $this->categoryId = 'Goals_Ecommerce'; } public function isEnabled() diff --git a/plugins/Ecommerce/Reports/BaseItem.php b/plugins/Ecommerce/Reports/BaseItem.php index 5caf770a6b..5896710dc2 100644 --- a/plugins/Ecommerce/Reports/BaseItem.php +++ b/plugins/Ecommerce/Reports/BaseItem.php @@ -9,15 +9,18 @@ namespace Piwik\Plugins\Ecommerce\Reports; use Piwik\Common; +use Piwik\DataTable; use Piwik\Metrics\Formatter; use Piwik\Piwik; -use Piwik\Plugin\Report; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; -use Piwik\Plugins\Goals\Goals; use Piwik\Plugins\Goals\Columns\Metrics\AveragePrice; use Piwik\Plugins\Goals\Columns\Metrics\AverageQuantity; use Piwik\Plugins\Goals\Columns\Metrics\ProductConversionRate; +use Piwik\Plugins\Goals\Conversions; +use Piwik\Plugins\Goals\Model; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; abstract class BaseItem extends Base { @@ -46,7 +49,8 @@ abstract class BaseItem extends Base public function getMetricsDocumentation() { - if ($this->isAbandonedCart()) { + // we do not check whether it is abondon carts if not set re performance improvements + if ($this->isAbandonedCart($fetchIfNotSet = false)) { return array( 'revenue' => Piwik::translate('Goals_ColumnRevenueDocumentation', Piwik::translate('Goals_DocumentationRevenueGeneratedByProductSales')), @@ -62,6 +66,11 @@ abstract class BaseItem extends Base return array(); } + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addToContainerWidget('Products', $factory->createWidget()); + } + public function configureView(ViewDataTable $view) { $idSite = Common::getRequestVar('idSite'); @@ -100,7 +109,7 @@ abstract class BaseItem extends Base $view->config->custom_parameters['viewDataTable'] = 'table'; $abandonedCart = true; } else { - $abandonedCart = $this->isAbandonedCart(); + $abandonedCart = $this->isAbandonedCart($fetchIfNotSet = true); } if ($abandonedCart) { @@ -127,8 +136,32 @@ abstract class BaseItem extends Base $view->config->columns_to_display = $columnsOrdered; } - private function isAbandonedCart() + private function isAbandonedCart($fetchIfNotSet) { - return Common::getRequestVar('abandonedCarts', '0', 'string') == 1; + $abandonedCarts = Common::getRequestVar('abandonedCarts', '', 'string'); + + if ($abandonedCarts === '') { + if ($fetchIfNotSet) { + + $idSite = Common::getRequestVar('idSite', 0, 'int'); + $period = Common::getRequestVar('period', '', 'string'); + $date = Common::getRequestVar('date', '', 'string'); + + $conversion = new Conversions(); + $conversions = $conversion->getConversionForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER, $idSite, $period, $date); + $cartNbConversions = $conversion->getConversionForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART, $idSite, $period, $date); + $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0; + + if ($preloadAbandonedCart) { + $abandonedCarts = '1'; + } else { + $abandonedCarts = '0'; + } + } else { + $abandonedCarts = '0'; + } + } + + return $abandonedCarts == '1'; } } diff --git a/plugins/Ecommerce/Reports/GetItemsCategory.php b/plugins/Ecommerce/Reports/GetItemsCategory.php index bf652ca10d..efd4a9489a 100644 --- a/plugins/Ecommerce/Reports/GetItemsCategory.php +++ b/plugins/Ecommerce/Reports/GetItemsCategory.php @@ -20,6 +20,7 @@ class GetItemsCategory extends BaseItem $this->name = Piwik::translate('Goals_ProductCategory'); $this->dimension = new ProductCategory(); $this->order = 32; - $this->widgetTitle = 'Goals_ProductCategory'; + + $this->subcategoryId = 'Goals_Products'; } } diff --git a/plugins/Ecommerce/Reports/GetItemsName.php b/plugins/Ecommerce/Reports/GetItemsName.php index a320b760e4..db8c7f5d3d 100644 --- a/plugins/Ecommerce/Reports/GetItemsName.php +++ b/plugins/Ecommerce/Reports/GetItemsName.php @@ -20,6 +20,7 @@ class GetItemsName extends BaseItem $this->name = Piwik::translate('Goals_ProductName'); $this->dimension = new ProductName(); $this->order = 31; - $this->widgetTitle = 'Goals_ProductName'; + + $this->subcategoryId = 'Goals_Products'; } } diff --git a/plugins/Ecommerce/Reports/GetItemsSku.php b/plugins/Ecommerce/Reports/GetItemsSku.php index a2b20eb3f9..9056b2ff6c 100644 --- a/plugins/Ecommerce/Reports/GetItemsSku.php +++ b/plugins/Ecommerce/Reports/GetItemsSku.php @@ -21,7 +21,8 @@ class GetItemsSku extends BaseItem $this->name = Piwik::translate('Goals_ProductSKU'); $this->dimension = new ProductSku(); $this->order = 30; - $this->widgetTitle = 'Goals_ProductSKU'; + + $this->subcategoryId = 'Goals_Products'; } } diff --git a/plugins/Ecommerce/Widgets.php b/plugins/Ecommerce/Widgets.php deleted file mode 100644 index e20a73850e..0000000000 --- a/plugins/Ecommerce/Widgets.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Ecommerce; - -use Piwik\Common; -use Piwik\Site; -use Piwik\Piwik; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'Goals_Ecommerce'; - - protected function init() - { - $idSite = $this->getIdSite(); - - $site = new Site($idSite); - if ($site->isEcommerceEnabled()) { - $this->addWidget('General_Overview', 'widgetGoalReport', array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER)); - $this->addWidget('Goals_EcommerceLog', 'getEcommerceLog'); - } - } - - private function getIdSite() - { - return Common::getRequestVar('idSite', null, 'int'); - } - -} diff --git a/plugins/Ecommerce/Widgets/GetEcommerceLog.php b/plugins/Ecommerce/Widgets/GetEcommerceLog.php new file mode 100644 index 0000000000..d1a68a435a --- /dev/null +++ b/plugins/Ecommerce/Widgets/GetEcommerceLog.php @@ -0,0 +1,28 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Widgets; + +use Piwik\Common; +use Piwik\Widget\WidgetConfig; +use Piwik\Site; + +class GetEcommerceLog extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Goals_Ecommerce'); + $config->setSubcategoryId('Goals_EcommerceLog'); + $config->setName('Goals_EcommerceLog'); + + $idSite = Common::getRequestVar('idSite', null, 'int'); + $site = new Site($idSite); + $config->setIsEnabled($site->isEcommerceEnabled()); + } + +} diff --git a/plugins/Ecommerce/Widgets/ProductsByDimension.php b/plugins/Ecommerce/Widgets/ProductsByDimension.php new file mode 100644 index 0000000000..2f25e23b76 --- /dev/null +++ b/plugins/Ecommerce/Widgets/ProductsByDimension.php @@ -0,0 +1,34 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Ecommerce\Widgets; + +use Piwik\Common; +use Piwik\Plugins\CoreHome\CoreHome; +use Piwik\Site; +use Piwik\Widget\WidgetContainerConfig; + +class ProductsByDimension extends WidgetContainerConfig +{ + protected $layout = CoreHome::WIDGET_CONTAINER_LAYOUT_BY_DIMENSION; + protected $id = 'Products'; + protected $categoryId = 'Goals_Ecommerce'; + protected $subcategoryId = 'Goals_Products'; + + public function isEnabled() + { + $idSite = Common::getRequestVar('idSite', false, 'int'); + + if (empty($idSite)) { + return false; + } + + $site = new Site($idSite); + return $site->isEcommerceEnabled(); + } +} diff --git a/plugins/Ecommerce/templates/conversionOverview.twig b/plugins/Ecommerce/templates/conversionOverview.twig new file mode 100644 index 0000000000..9b6864c03c --- /dev/null +++ b/plugins/Ecommerce/templates/conversionOverview.twig @@ -0,0 +1,15 @@ +<ul class="ulGoalTopElements"> + {{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}} + {% if revenue_subtotal is not empty %}, + {{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}} + {% endif %} + {%- if revenue_tax is not empty -%}, + {{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}} + {% endif %} + {%- if revenue_shipping is not empty -%}, + {{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}} + {% endif %} + {%- if revenue_discount is not empty -%}, + {{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}} + {% endif %} +</ul><br/>
\ No newline at end of file diff --git a/plugins/Ecommerce/templates/ecommerceLog.twig b/plugins/Ecommerce/templates/ecommerceLog.twig deleted file mode 100644 index 4c40a94d99..0000000000 --- a/plugins/Ecommerce/templates/ecommerceLog.twig +++ /dev/null @@ -1,3 +0,0 @@ -<h2 piwik-enriched-headline>{{ 'Goals_EcommerceLog'|translate }}</h2> - -{{ ecommerceLog|raw }} diff --git a/plugins/Ecommerce/templates/getSparklines.twig b/plugins/Ecommerce/templates/getSparklines.twig new file mode 100644 index 0000000000..0952c081a5 --- /dev/null +++ b/plugins/Ecommerce/templates/getSparklines.twig @@ -0,0 +1,55 @@ +<div id='leftcolumn' style="clear:both;{% if not isWidget %}width:33%;'{% endif %}"> + <div class="sparkline">{{ sparkline(urlSparklineConversions) }} + <strong>{{ nb_conversions }}</strong> + {{ 'General_EcommerceOrders'|translate }} + <img src='plugins/Morpheus/images/ecommerceOrder.gif'> + + {% if goalAllowMultipleConversionsPerVisit is defined and goalAllowMultipleConversionsPerVisit %} + ({{ 'General_NVisits'|translate("<strong>"~nb_visits_converted~"</strong>")|raw }}) + {% endif %} + </div> + + <div class="sparkline"> + {{ sparkline(urlSparklineRevenue) }} + {% set revenue=revenue|money(idSite) %} + <strong>{{ revenue|raw }}</strong> {{ 'General_TotalRevenue'|translate }} + </div> + + <div class="sparkline">{{ sparkline(urlSparklineAverageOrderValue) }} + <strong>{{ avg_order_revenue|money(idSite)|raw }}</strong> + {{ 'General_AverageOrderValue'|translate }} + </div> +</div> +<div id='leftcolumn' {% if not isWidget %}style='width:33%;'{% endif %}> + <div class="sparkline">{{ sparkline(urlSparklineConversionRate) }} + {% set ecommerceOrdersText %}{{ 'General_EcommerceOrders'|translate }}{% endset %} + {{ 'Goals_ConversionRate'|translate("<strong>"~conversion_rate~"</strong> "~ecommerceOrdersText)|raw }} + </div> + <div class="sparkline">{{ sparkline(urlSparklinePurchasedProducts) }} + <strong>{{ items }}</strong> {{ 'General_PurchasedProducts'|translate }}</div> +</div> +<div id='rightcolumn' {% if not isWidget %}style='width:30%;'{% endif %}> + <div> + <img src='plugins/Morpheus/images/ecommerceAbandonedCart.gif'> <em>{{ 'General_AbandonedCarts'|translate }}</em> + </div> + + <div class="sparkline"> + {{ sparkline(cart_urlSparklineConversions) }} + {% set ecommerceAbandonedCartsText %}{{ 'Goals_AbandonedCart'|translate }}{% endset %} + <strong>{{ cart_nb_conversions }}</strong> {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }} + </div> + + <div class="sparkline"> + {{ sparkline(cart_urlSparklineRevenue) }} + {% set revenue %}{{ cart_revenue|money(idSite)|raw }}{% endset %} + {% set revenueText %}{{ 'General_ColumnRevenue'|translate }}{% endset %} + <strong>{{ revenue }}</strong> {{ 'Goals_LeftInCart'|translate(revenueText) }} + </div> + + <div class="sparkline"> + {{ sparkline(cart_urlSparklineConversionRate) }} + <strong>{{ cart_conversion_rate }}</strong> + {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }} + </div> +</div> +{% include "_sparklineFooter.twig" %} diff --git a/plugins/Events/Categories/EventsSubcategory.php b/plugins/Events/Categories/EventsSubcategory.php new file mode 100644 index 0000000000..e969029e13 --- /dev/null +++ b/plugins/Events/Categories/EventsSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Events\Categories; + +use Piwik\Category\Subcategory; + +class EventsSubcategory extends Subcategory +{ + protected $categoryId = 'General_Actions'; + protected $id = 'Events_Events'; + protected $order = 40; + +} diff --git a/plugins/Events/Controller.php b/plugins/Events/Controller.php deleted file mode 100644 index 3429443b88..0000000000 --- a/plugins/Events/Controller.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Events; - -use Piwik\View; - -/** - * Events controller - * - */ -class Controller extends \Piwik\Plugin\Controller -{ - public function index() - { - $view = new View('@Events/index'); - $view->leftMenuReports = $this->getLeftMenuReports(); - return $view->render(); - } - - private function getLeftMenuReports() - { - $reports = new View\ReportsByDimension('Events'); - foreach(Events::getLabelTranslations() as $apiAction => $translations) { - // 'getCategory' is the API method, but we are loading 'indexCategory' which displays <h2> - $count = 1; - $controllerAction = str_replace("get", "index", $apiAction, $count); - $params = array( - 'secondaryDimension' => API::getInstance()->getDefaultSecondaryDimension($apiAction) - ); - $reports->addReport('Events_TopEvents', $translations[0], 'Events.' . $controllerAction, $params); - } - return $reports->render(); - } - - public function indexCategory() - { - return $this->indexEvent(__FUNCTION__); - } - - public function indexAction() - { - return $this->indexEvent(__FUNCTION__); - } - - public function indexName() - { - return $this->indexEvent(__FUNCTION__); - } - - public function getActionFromCategoryId() - { - return $this->renderReport(__FUNCTION__); - } - - public function getNameFromCategoryId() - { - return $this->renderReport(__FUNCTION__); - } - - public function getCategoryFromActionId() - { - return $this->renderReport(__FUNCTION__); - } - - public function getNameFromActionId() - { - return $this->renderReport(__FUNCTION__); - } - - public function getActionFromNameId() - { - return $this->renderReport(__FUNCTION__); - } - - public function getCategoryFromNameId() - { - return $this->renderReport(__FUNCTION__); - } - - protected function indexEvent($controllerMethod) - { - $count = 1; - $apiMethod = str_replace('index', 'get', $controllerMethod, $count); - $events = new Events; - $title = $events->getReportTitleTranslation($apiMethod); - - if (method_exists($this, $apiMethod)) { - $content = $this->$apiMethod(); - } else { - $content = $this->renderReport($apiMethod); - } - - return View::singleReport( - $title, - $content - ); - } -} diff --git a/plugins/Events/Events.php b/plugins/Events/Events.php index ba3e96465d..f09e1a9925 100644 --- a/plugins/Events/Events.php +++ b/plugins/Events/Events.php @@ -12,6 +12,7 @@ use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugin\ViewDataTable; +use Piwik\Plugin\Reports; class Events extends \Piwik\Plugin { @@ -154,7 +155,7 @@ class Events extends \Piwik\Plugin $this->addRelatedReports($view, $secondaryDimension); $this->addTooltipEventValue($view); - $subtableReport = Report::factory('Events', $view->config->subtable_controller_action); + $subtableReport = Reports::factory('Events', $view->config->subtable_controller_action); $view->config->pivot_by_dimension = $subtableReport->getDimension()->getId(); $view->config->pivot_by_column = 'nb_events'; } diff --git a/plugins/Events/Menu.php b/plugins/Events/Menu.php deleted file mode 100644 index 440a35978e..0000000000 --- a/plugins/Events/Menu.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Events; - -use Piwik\Menu\MenuReporting; - -/** - */ -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addActionsItem('Events_Events', $this->urlForAction('index'), 30); - } -} diff --git a/plugins/Events/Reports/Base.php b/plugins/Events/Reports/Base.php index 4430b31407..a43ba0dbfa 100644 --- a/plugins/Events/Reports/Base.php +++ b/plugins/Events/Reports/Base.php @@ -10,19 +10,31 @@ namespace Piwik\Plugins\Events\Reports; use Piwik\Plugins\Events\API; use Piwik\Plugins\Events\Columns\Metrics\AverageEventValue; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'General_Actions'; + $this->subcategoryId = 'Events_Events'; + $this->processedMetrics = array( new AverageEventValue() ); + } - $this->widgetParams = array( - 'secondaryDimension' => API::getInstance()->getDefaultSecondaryDimension($this->action) - ); + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + if (!$this->isSubtableReport) { + $widget = $factory->createWidget()->setParameters(array( + 'secondaryDimension' => API::getInstance()->getDefaultSecondaryDimension($this->action) + )); + + $widgetsList->addToContainerWidget('Events', $widget); + } } + } diff --git a/plugins/Events/Reports/GetAction.php b/plugins/Events/Reports/GetAction.php index 84f750e264..bbdadd7ed4 100644 --- a/plugins/Events/Reports/GetAction.php +++ b/plugins/Events/Reports/GetAction.php @@ -27,6 +27,5 @@ class GetAction extends Base $this->actionToLoadSubTables = 'getNameFromActionId'; } $this->order = 1; - $this->widgetTitle = 'Events_EventActions'; } } diff --git a/plugins/Events/Reports/GetActionFromCategoryId.php b/plugins/Events/Reports/GetActionFromCategoryId.php index e5de6af9ca..9c355d4de1 100644 --- a/plugins/Events/Reports/GetActionFromCategoryId.php +++ b/plugins/Events/Reports/GetActionFromCategoryId.php @@ -19,7 +19,7 @@ class GetActionFromCategoryId extends Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'Events_Events'; $this->processedMetrics = false; $this->dimension = new EventAction(); $this->name = Piwik::translate('Events_EventActions'); diff --git a/plugins/Events/Reports/GetActionFromNameId.php b/plugins/Events/Reports/GetActionFromNameId.php index 55bd628459..1da175e47b 100644 --- a/plugins/Events/Reports/GetActionFromNameId.php +++ b/plugins/Events/Reports/GetActionFromNameId.php @@ -19,7 +19,7 @@ class GetActionFromNameId extends Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'Events_Events'; $this->processedMetrics = false; $this->dimension = new EventAction(); $this->name = Piwik::translate('Events_EventActions'); diff --git a/plugins/Events/Reports/GetCategory.php b/plugins/Events/Reports/GetCategory.php index be867705ea..2049868e7c 100644 --- a/plugins/Events/Reports/GetCategory.php +++ b/plugins/Events/Reports/GetCategory.php @@ -27,6 +27,5 @@ class GetCategory extends Base $this->actionToLoadSubTables = 'getActionFromCategoryId'; } $this->order = 0; - $this->widgetTitle = 'Events_EventCategories'; } } diff --git a/plugins/Events/Reports/GetCategoryFromActionId.php b/plugins/Events/Reports/GetCategoryFromActionId.php index 51f5f6918d..47e62c9260 100644 --- a/plugins/Events/Reports/GetCategoryFromActionId.php +++ b/plugins/Events/Reports/GetCategoryFromActionId.php @@ -19,7 +19,7 @@ class GetCategoryFromActionId extends Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'Events_Events'; $this->processedMetrics = false; $this->dimension = new EventCategory(); $this->name = Piwik::translate('Events_EventCategories'); diff --git a/plugins/Events/Reports/GetCategoryFromNameId.php b/plugins/Events/Reports/GetCategoryFromNameId.php index 4806c97d0f..0eef6b5f32 100644 --- a/plugins/Events/Reports/GetCategoryFromNameId.php +++ b/plugins/Events/Reports/GetCategoryFromNameId.php @@ -19,7 +19,7 @@ class GetCategoryFromNameId extends Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'Events_Events'; $this->processedMetrics = false; $this->dimension = new EventCategory(); $this->name = Piwik::translate('Events_EventCategories'); diff --git a/plugins/Events/Reports/GetName.php b/plugins/Events/Reports/GetName.php index aa21f968be..08d97b6ac4 100644 --- a/plugins/Events/Reports/GetName.php +++ b/plugins/Events/Reports/GetName.php @@ -27,6 +27,5 @@ class GetName extends Base $this->actionToLoadSubTables = 'getActionFromNameId'; } $this->order = 2; - $this->widgetTitle = 'Events_EventNames'; } } diff --git a/plugins/Events/Reports/GetNameFromActionId.php b/plugins/Events/Reports/GetNameFromActionId.php index 7b4899b672..4c0325a706 100644 --- a/plugins/Events/Reports/GetNameFromActionId.php +++ b/plugins/Events/Reports/GetNameFromActionId.php @@ -19,7 +19,7 @@ class GetNameFromActionId extends Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'Events_Events'; $this->processedMetrics = false; $this->dimension = new EventName(); $this->name = Piwik::translate('Events_Names'); diff --git a/plugins/Events/Reports/GetNameFromCategoryId.php b/plugins/Events/Reports/GetNameFromCategoryId.php index d1f43f7931..6e28ccf1e9 100644 --- a/plugins/Events/Reports/GetNameFromCategoryId.php +++ b/plugins/Events/Reports/GetNameFromCategoryId.php @@ -19,7 +19,7 @@ class GetNameFromCategoryId extends Report { protected function init() { - $this->category = 'Events_Events'; + $this->categoryId = 'Events_Events'; $this->processedMetrics = false; $this->dimension = new EventName(); $this->name = Piwik::translate('Events_EventNames'); diff --git a/plugins/Events/Widgets/EventsByDimension.php b/plugins/Events/Widgets/EventsByDimension.php new file mode 100644 index 0000000000..ef04e0d761 --- /dev/null +++ b/plugins/Events/Widgets/EventsByDimension.php @@ -0,0 +1,22 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Events\Widgets; + +use Piwik\Plugins\CoreHome\CoreHome; +use Piwik\Translation\Translator; +use Piwik\Widget\WidgetContainerConfig; + +class EventsByDimension extends WidgetContainerConfig +{ + protected $layout = CoreHome::WIDGET_CONTAINER_LAYOUT_BY_DIMENSION; + protected $id = 'Events'; + protected $categoryId = 'General_Actions'; + protected $subcategoryId = 'Events_Events'; + +} diff --git a/plugins/Events/templates/index.twig b/plugins/Events/templates/index.twig deleted file mode 100644 index a12afb651a..0000000000 --- a/plugins/Events/templates/index.twig +++ /dev/null @@ -1,2 +0,0 @@ -{{ leftMenuReports|raw }} - diff --git a/plugins/ExamplePlugin/Menu.php b/plugins/ExamplePlugin/Menu.php index f890587fea..7c9acb90f6 100644 --- a/plugins/ExamplePlugin/Menu.php +++ b/plugins/ExamplePlugin/Menu.php @@ -9,27 +9,16 @@ namespace Piwik\Plugins\ExamplePlugin; use Piwik\Menu\MenuAdmin; -use Piwik\Menu\MenuReporting; use Piwik\Menu\MenuTop; use Piwik\Menu\MenuUser; /** * This class allows you to add, remove or rename menu items. - * To configure a menu (such as Admin Menu, Reporting Menu, User Menu...) simply call the corresponding methods as + * To configure a menu (such as Admin Menu, Top Menu, User Menu...) simply call the corresponding methods as * described in the API-Reference http://developer.piwik.org/api-reference/Piwik/Menu/MenuAbstract */ class Menu extends \Piwik\Plugin\Menu { - public function configureReportingMenu(MenuReporting $menu) - { - // reuse an existing category. Execute the showList() method within the controller when menu item was clicked - // $menu->addVisitorsItem('Report 1', $this->urlForAction('showList'), $orderId = 30); - // $menu->addActionsItem('Report 1', $this->urlForAction('showList'), $orderId = 30); - - // or create a custom category 'UI Framework' - // $menu->addItem('UI Framework', '', $this->urlForDefaultAction(), $orderId = 30); - // $menu->addItem('UI Framework', 'Report 1', $this->urlForAction('showList'), $orderId = 30); - } public function configureAdminMenu(MenuAdmin $menu) { diff --git a/plugins/ExamplePlugin/Widgets.php b/plugins/ExamplePlugin/Widgets.php deleted file mode 100644 index a5670902ef..0000000000 --- a/plugins/ExamplePlugin/Widgets.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ - -namespace Piwik\Plugins\ExamplePlugin; - -use Piwik\View; -use Piwik\WidgetsList; - -/** - * This class allows you to add your own widgets to the Piwik platform. In case you want to remove widgets from another - * plugin please have a look at the "configureWidgetsList()" method. - * To configure a widget simply call the corresponding methods as described in the API-Reference: - * http://developer.piwik.org/api-reference/Piwik/Plugin\Widgets - */ -class Widgets extends \Piwik\Plugin\Widgets -{ - /** - * Here you can define the category the widget belongs to. You can reuse any existing widget category or define - * your own category. - * @var string - */ - protected $category = 'Example Category'; - - /** - * Here you can add one or multiple widgets. You can add a widget by calling the method "addWidget()" and pass the - * name of the widget as well as a method name that should be called to render the widget. The method can be - * defined either directly here in this widget class or in the controller in case you want to reuse the same action - * for instance in the menu etc. - */ - protected function init() - { - // $this->addWidget('Example Widget Name', $method = 'myExampleWidget'); - // $this->addWidget('Example Widget 2', $method = 'myExampleWidget', $params = array('myparam' => 'myvalue')); - } - - /** - * This method renders a widget as defined in "init()". It's on you how to generate the content of the - * widget. As long as you return a string everything is fine. You can use for instance a "Piwik\View" to render a - * twig template. In such a case don't forget to create a twig template (eg. myViewTemplate.twig) in the - * "templates" directory of your plugin. - * - * @return string - */ - public function myExampleWidget() - { - // $view = new View('@ExamplePlugin/myViewTemplate'); - // return $view->render(); - - return 'My Widget Text'; - } - - /** - * Here you can remove any widgets defined by any plugin. - * - * @param WidgetsList $widgetsList - */ - public function configureWidgetsList(WidgetsList $widgetsList) - { - // $widgetsList->remove('NameOfWidgetCategory'); // will remove all widgets having this category - // $widgetsList->remove('NameOfWidgetCategory', 'Widget name'); // will only remove a specific widget - } -} diff --git a/plugins/ExamplePlugin/Widgets/MyExampleWidget.php b/plugins/ExamplePlugin/Widgets/MyExampleWidget.php new file mode 100644 index 0000000000..687ee6df67 --- /dev/null +++ b/plugins/ExamplePlugin/Widgets/MyExampleWidget.php @@ -0,0 +1,80 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\ExamplePlugin\Widgets; + +use Piwik\Widget\Widget; +use Piwik\Widget\WidgetConfig; +use Piwik\View; + +/** + * This class allows you to add your own widget to the Piwik platform. In case you want to remove widgets from another + * plugin please have a look at the "configureWidgetsList()" method. + * To configure a widget simply call the corresponding methods as described in the API-Reference: + * http://developer.piwik.org/api-reference/Piwik/Plugin\Widget + */ +class MyExampleWidget extends Widget +{ + public static function configure(WidgetConfig $config) + { + /** + * Set the category the widget belongs to. You can reuse any existing widget category or define + * your own category. + */ + $config->setCategoryId('Example Widgets'); + + /** + * Set the subcategory the widget belongs to. If a subcategory is set, the widget will be shown in the UI. + */ + // $config->setSubcategoryId('General_Overview'); + + /** + * Set the name of the widget belongs to. + */ + $config->setName('Example Widget Name'); + + /** + * Set the order of the widget. The lower the number, the earlier the widget will be listed within a category. + */ + $config->setOrder(99); + + /** + * Optionally set URL parameters that will be used when this widget is requested. + * $config->setParameters(array('myparam' => 'myvalue')); + */ + + /** + * Define whether a widget is enabled or not. For instance some widgets might not be available to every user or + * might depend on a setting (such as Ecommerce) of a site. In such a case you can perform any checks and then + * set `true` or `false`. If your widget is only available to users having super user access you can do the + * following: + * + * $config->setIsEnabled(\Piwik\Piwik::hasUserSuperUserAccess()); + * or + * if (!\Piwik\Piwik::hasUserSuperUserAccess()) + * $config->disable(); + */ + } + + /** + * This method renders the widget. It's on you how to generate the content of the widget. + * As long as you return a string everything is fine. You can use for instance a "Piwik\View" to render a + * twig template. In such a case don't forget to create a twig template (eg. myViewTemplate.twig) in the + * "templates" directory of your plugin. + * + * @return string + */ + public function render() + { + // $view = new View('@ExamplePlugin/myViewTemplate'); + // return $view->render(); + + return '<div>My Widget Text</div>'; + } + +}
\ No newline at end of file diff --git a/plugins/ExampleReport/Reports/Base.php b/plugins/ExampleReport/Reports/Base.php index c09da78e4b..35af935d9e 100644 --- a/plugins/ExampleReport/Reports/Base.php +++ b/plugins/ExampleReport/Reports/Base.php @@ -14,6 +14,6 @@ abstract class Base extends Report { protected function init() { - $this->category = 'ExampleCategory'; + $this->categoryId = 'ExampleCategory'; } } diff --git a/plugins/ExampleReport/Reports/GetExampleReport.php b/plugins/ExampleReport/Reports/GetExampleReport.php index 42af54d04a..9e0980f050 100644 --- a/plugins/ExampleReport/Reports/GetExampleReport.php +++ b/plugins/ExampleReport/Reports/GetExampleReport.php @@ -49,12 +49,8 @@ class GetExampleReport extends Base // 24 rows for 1-24hours // $this->constantRowsCount = true; - // If a menu title is specified, the report will be displayed in the menu - // $this->menuTitle = 'ExampleReportName'; - - // If a widget title is specified, the report will be displayed in the list of widgets and the report can be - // exported as a widget - // $this->widgetTitle = 'ExampleReportName'; + // If a subcategory is specified, the report will be displayed in the menu under this menu item + // $this->subCategory = 'ExampleReportName'; } /** diff --git a/plugins/ExampleRssWidget/Widgets.php b/plugins/ExampleRssWidget/Widgets/RssChangelog.php index 5fc0667fa8..70ba5eb0c0 100644 --- a/plugins/ExampleRssWidget/Widgets.php +++ b/plugins/ExampleRssWidget/Widgets/RssChangelog.php @@ -6,35 +6,21 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later * */ -namespace Piwik\Plugins\ExampleRssWidget; +namespace Piwik\Plugins\ExampleRssWidget\Widgets; use Piwik\Piwik; +use Piwik\Widget\WidgetConfig; +use Piwik\Plugins\ExampleRssWidget\RssRenderer; -class Widgets extends \Piwik\Plugin\Widgets +class RssChangelog extends \Piwik\Widget\Widget { - protected $category = 'Example Widgets'; - - protected function init() - { - $this->addWidget('Piwik.org Blog', 'rssPiwik'); - $this->addWidget('Piwik Changelog', 'rssChangelog'); - } - - public function rssPiwik() + public static function configure(WidgetConfig $config) { - try { - $rss = new RssRenderer('http://feeds.feedburner.com/Piwik'); - $rss->showDescription(true); - - return $rss->get(); - - } catch (\Exception $e) { - - return $this->error($e); - } + $config->setCategoryId('Example Widgets'); + $config->setName('Piwik Changelog'); } - public function rssChangelog() + public function render() { try { $rss = new RssRenderer('http://feeds.feedburner.com/PiwikReleases'); diff --git a/plugins/ExampleRssWidget/Widgets/RssPiwik.php b/plugins/ExampleRssWidget/Widgets/RssPiwik.php new file mode 100644 index 0000000000..c3c7c76a16 --- /dev/null +++ b/plugins/ExampleRssWidget/Widgets/RssPiwik.php @@ -0,0 +1,47 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\ExampleRssWidget\Widgets; + +use Piwik\Piwik; +use Piwik\Widget\WidgetConfig; +use Piwik\Plugins\ExampleRssWidget\RssRenderer; + +class RssPiwik extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Example Widgets'); + $config->setName('Piwik.org Blog'); + } + + public function render() + { + try { + $rss = new RssRenderer('http://feeds.feedburner.com/Piwik'); + $rss->showDescription(true); + + return $rss->get(); + + } catch (\Exception $e) { + + return $this->error($e); + } + } + + /** + * @param \Exception $e + * @return string + */ + private function error($e) + { + return '<div class="pk-emptyDataTable">' + . Piwik::translate('General_ErrorRequest', array('', '')) + . ' - ' . $e->getMessage() . '</div>'; + } +} diff --git a/plugins/ExampleUI/API.php b/plugins/ExampleUI/API.php index 7b6e0c508a..ae9beae05f 100644 --- a/plugins/ExampleUI/API.php +++ b/plugins/ExampleUI/API.php @@ -99,4 +99,5 @@ class API extends \Piwik\Plugin\API return $planetsDataTable; } + } diff --git a/plugins/ExampleUI/Categories/ExampleUiCategory.php b/plugins/ExampleUI/Categories/ExampleUiCategory.php new file mode 100644 index 0000000000..cc7d2fd219 --- /dev/null +++ b/plugins/ExampleUI/Categories/ExampleUiCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\ExampleUI\Categories; + +use Piwik\Category\Category; + +class ExampleUiCategory extends Category +{ + protected $id = 'ExampleUI_UiFramework'; + protected $order = 90; +} diff --git a/plugins/ExampleUI/Controller.php b/plugins/ExampleUI/Controller.php index 210093c8b5..c032d449fa 100644 --- a/plugins/ExampleUI/Controller.php +++ b/plugins/ExampleUI/Controller.php @@ -18,38 +18,6 @@ use Piwik\ViewDataTable\Factory as ViewDataTableFactory; */ class Controller extends \Piwik\Plugin\Controller { - public function dataTables() - { - $controllerAction = $this->pluginName . '.' . __FUNCTION__; - $apiAction = 'ExampleUI.getTemperatures'; - - $view = ViewDataTableFactory::build('table', $apiAction, $controllerAction); - - $view->config->translations['value'] = 'Temperature in °C'; - $view->config->translations['label'] = 'Hour of day'; - $view->requestConfig->filter_sort_column = 'label'; - $view->requestConfig->filter_sort_order = 'asc'; - $view->requestConfig->filter_limit = 24; - $view->config->columns_to_display = array('label', 'value'); - $view->config->y_axis_unit = '°C'; // useful if the user requests the bar graph - $view->config->show_exclude_low_population = false; - $view->config->show_table_all_columns = false; - $view->config->disable_row_evolution = true; - $view->config->max_graph_elements = 24; - $view->config->metrics_documentation = array('value' => 'Documentation for temperature metric'); - - return $view->render(); - } - - public function evolutionGraph() - { - $view = new View('@ExampleUI/evolutiongraph'); - - $this->setPeriodVariablesView($view); - $view->evolutionGraph = $this->getEvolutionGraph(array(), array('server1', 'server2')); - - return $view->render(); - } public function notifications() { @@ -78,126 +46,4 @@ class Controller extends \Piwik\Plugin\Controller return $view->render(); } - public function getEvolutionGraph(array $columns = array(), array $defaultColumns = array()) - { - if (empty($columns)) { - $columns = Common::getRequestVar('columns', false); - if (false !== $columns) { - $columns = Piwik::getArrayFromApiParameter($columns); - } - } - - $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns, - $selectableColumns = array('server1', 'server2'), 'My documentation', 'ExampleUI.getTemperaturesEvolution'); - $view->requestConfig->filter_sort_column = 'label'; - $view->requestConfig->filter_sort_order = 'asc'; - - if (empty($view->config->columns_to_display) && !empty($defaultColumns)) { - $view->config->columns_to_display = $defaultColumns; - } - - return $this->renderView($view); - } - - public function barGraph() - { - $view = ViewDataTableFactory::build( - 'graphVerticalBar', 'ExampleUI.getTemperatures', $controllerAction = 'ExampleUI.barGraph'); - - $view->config->y_axis_unit = '°C'; - $view->config->show_footer = false; - $view->config->translations['value'] = "Temperature"; - $view->config->selectable_columns = array("value"); - $view->config->max_graph_elements = 24; - - return $view->render(); - } - - public function pieGraph() - { - $view = ViewDataTableFactory::build( - 'graphPie', 'ExampleUI.getPlanetRatios', $controllerAction = 'ExampleUI.pieGraph'); - - $view->config->columns_to_display = array('value'); - $view->config->translations['value'] = "times the diameter of Earth"; - $view->config->show_footer_icons = false; - $view->config->selectable_columns = array("value"); - $view->config->max_graph_elements = 10; - - return $view->render(); - } - - public function tagClouds() - { - $output = "<h2>Simple tag cloud</h2>"; - $output .= $this->echoSimpleTagClouds(); - - $output .= "<br /><br /><h2>Advanced tag cloud: with logos and links</h2> - <ul style='list-style-type:disc;margin-left:50px'> - <li>The logo size is proportional to the value returned by the API</li> - <li>The logo is linked to a specific URL</li> - </ul><br /><br />"; - $output .= $this->echoAdvancedTagClouds(); - - return $output; - } - - public function echoSimpleTagClouds() - { - $view = ViewDataTableFactory::build( - 'cloud', 'ExampleUI.getPlanetRatios', $controllerAction = 'ExampleUI.echoSimpleTagClouds'); - - $view->config->columns_to_display = array('label', 'value'); - $view->config->translations['value'] = "times the diameter of Earth"; - $view->config->show_footer = false; - - return $view->render(); - } - - public function echoAdvancedTagClouds() - { - $view = ViewDataTableFactory::build( - 'cloud', 'ExampleUI.getPlanetRatiosWithLogos', $controllerAction = 'ExampleUI.echoAdvancedTagClouds'); - - $view->config->display_logo_instead_of_label = true; - $view->config->columns_to_display = array('label', 'value'); - $view->config->translations['value'] = "times the diameter of Earth"; - - return $view->render(); - } - - public function sparklines() - { - $view = new View('@ExampleUI/sparklines'); - $view->urlSparkline1 = $this->getUrlSparkline('generateSparkline', array('server' => 'server1', 'rand' => mt_rand())); - $view->urlSparkline2 = $this->getUrlSparkline('generateSparkline', array('server' => 'server2', 'rand' => mt_rand())); - - return $view->render(); - } - - public function generateSparkline() - { - $view = ViewDataTableFactory::build( - 'sparkline', 'ExampleUI.getTemperaturesEvolution', $controllerAction = 'ExampleUI.generateSparkline'); - - $serverRequested = Common::getRequestVar('server', false); - if (false !== $serverRequested) { - $view->config->columns_to_display = array($serverRequested); - } - - return $view->render(); - } - - public function treemap() - { - $view = ViewDataTableFactory::build( - 'infoviz-treemap', 'ExampleUI.getTemperatures', $controllerAction = 'ExampleUI.treemap'); - - $view->config->translations['value'] = "Temperature"; - $view->config->columns_to_display = array("label", "value"); - $view->config->selectable_columns = array("value"); - $view->config->show_evolution_values = 0; - - return $view->render(); - } } diff --git a/plugins/ExampleUI/Menu.php b/plugins/ExampleUI/Menu.php index e25bd10817..35cf0d70b5 100644 --- a/plugins/ExampleUI/Menu.php +++ b/plugins/ExampleUI/Menu.php @@ -8,37 +8,12 @@ */ namespace Piwik\Plugins\ExampleUI; -use Piwik\Menu\MenuReporting; use Piwik\Menu\MenuUser; -use Piwik\Plugin\Manager as PluginManager; -/** - */ class Menu extends \Piwik\Plugin\Menu { - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addItem('UI Framework', '', $this->urlForAction('dataTables'), 30); - - $this->addSubMenu($menu, 'Data tables', 'dataTables', 1); - $this->addSubMenu($menu, 'Bar graph', 'barGraph', 2); - $this->addSubMenu($menu, 'Pie graph', 'pieGraph', 3); - $this->addSubMenu($menu, 'Tag clouds', 'tagClouds', 4); - $this->addSubMenu($menu, 'Sparklines', 'sparklines', 5); - $this->addSubMenu($menu, 'Evolution Graph', 'evolutionGraph', 6); - - if (PluginManager::getInstance()->isPluginActivated('TreemapVisualization')) { - $this->addSubMenu($menu, 'Treemap', 'treemap', 7); - } - } - public function configureUserMenu(MenuUser $menu) { $menu->addPlatformItem('UI Notifications', $this->urlForAction('notifications'), $order = 10); } - - private function addSubMenu(MenuReporting $menu, $subMenu, $action, $order) - { - $menu->addItem('UI Framework', $subMenu, $this->urlForAction($action), $order); - } } diff --git a/plugins/ExampleUI/Reports/Base.php b/plugins/ExampleUI/Reports/Base.php new file mode 100644 index 0000000000..88fa676754 --- /dev/null +++ b/plugins/ExampleUI/Reports/Base.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\ExampleUI\Reports; + +use Piwik\Plugin\Report; + +abstract class Base extends Report +{ + protected function init() + { + $this->categoryId = 'ExampleUI_UiFramework'; + } +} diff --git a/plugins/ExampleUI/Reports/GetPlanetRatios.php b/plugins/ExampleUI/Reports/GetPlanetRatios.php new file mode 100644 index 0000000000..244e8f79c8 --- /dev/null +++ b/plugins/ExampleUI/Reports/GetPlanetRatios.php @@ -0,0 +1,74 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleUI\Reports; + +use Piwik\Plugin\Report; +use Piwik\Plugin\ViewDataTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\Cloud; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Pie; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; + +/** + * This class defines a new report. + * + * See {@link http://developer.piwik.org/api-reference/Piwik/Plugin/Report} for more information. + */ +class GetPlanetRatios extends Base +{ + protected function init() + { + parent::init(); + + $this->name = 'Pie graph'; + $this->subcategoryId = $this->name; + $this->order = 112; + } + + public function getDefaultTypeViewDataTable() + { + return PIE::ID; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + // in this case it will render PIE as configured as default + $factory->createWidget() + ); + + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('Simple tag cloud') + ->setSubcategoryId('Tag clouds') + ->forceViewDataTable(Cloud::ID) + ->setOrder(5) + ); + } + + public function configureView(ViewDataTable $view) + { + $view->config->addTranslation('value', 'times the diameter of Earth'); + + if ($view->isViewDataTableId(PIE::ID)) { + + $view->config->columns_to_display = array('value'); + $view->config->selectable_columns = array('value'); + $view->config->show_footer_icons = false; + $view->config->max_graph_elements = 10; + + } else if ($view->isViewDataTableId(Cloud::ID)) { + + $view->config->columns_to_display = array('label', 'value'); + $view->config->show_footer = false; + + } + } + +} diff --git a/plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php b/plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php new file mode 100644 index 0000000000..d4d205e6c9 --- /dev/null +++ b/plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php @@ -0,0 +1,44 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleUI\Reports; + +use Piwik\Piwik; +use Piwik\Plugin\Report; +use Piwik\Plugin\ViewDataTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\Cloud; + +/** + * This class defines a new report. + * + * See {@link http://developer.piwik.org/api-reference/Piwik/Plugin/Report} for more information. + */ +class GetPlanetRatiosWithLogos extends Base +{ + protected function init() + { + parent::init(); + + $this->name = Piwik::translate('Advanced tag cloud: with logos and links'); + $this->subcategoryId = 'Tag clouds'; + $this->order = 113; + } + + public function getDefaultTypeViewDataTable() + { + return Cloud::ID; + } + + public function configureView(ViewDataTable $view) + { + $view->config->display_logo_instead_of_label = true; + $view->config->columns_to_display = array('label', 'value'); + $view->config->addTranslation('value', 'times the diameter of Earth'); + } + +} diff --git a/plugins/ExampleUI/Reports/GetTemperatures.php b/plugins/ExampleUI/Reports/GetTemperatures.php new file mode 100644 index 0000000000..0449e8b3e2 --- /dev/null +++ b/plugins/ExampleUI/Reports/GetTemperatures.php @@ -0,0 +1,93 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleUI\Reports; + +use Piwik\Piwik; +use Piwik\Plugin\Report; +use Piwik\Plugin\ViewDataTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Bar; +use Piwik\Plugin\Manager as PluginManager; +use Piwik\Report\ReportWidgetFactory; +use Piwik\View; +use Piwik\Widget\WidgetsList; + +/** + * This class defines a new report. + * + * See {@link http://developer.piwik.org/api-reference/Piwik/Plugin/Report} for more information. + */ +class GetTemperatures extends Base +{ + protected function init() + { + parent::init(); + + $this->name = Piwik::translate('ExampleUI_GetTemperaturesDataTable'); + $this->subcategoryId = 'ExampleUI_GetTemperaturesDataTable'; + $this->order = 110; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + // this will render the default view, in this case an Html Table + $widgetsList->addWidgetConfig($factory->createWidget()); + + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->forceViewDataTable(Bar::ID) + ->setSubcategoryId('Bar graph') + ); + + if (PluginManager::getInstance()->isPluginActivated('TreemapVisualization')) { + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('Treemap example') + ->setSubcategoryId('Treemap') + ->forceViewDataTable('infoviz-treemap') + ); + + } + } + + public function configureView(ViewDataTable $view) + { + if ($view->isViewDataTableId(BAR::ID)) { + + $view->config->y_axis_unit = '°C'; + $view->config->show_footer = false; + $view->config->translations['value'] = "Temperature"; + $view->config->selectable_columns = array("value"); + $view->config->max_graph_elements = 24; + + } elseif ($view->isViewDataTableId('infoviz-treemap')) { + + $view->config->translations['value'] = "Temperature"; + $view->config->columns_to_display = array("label", "value"); + $view->config->selectable_columns = array("value"); + $view->config->show_evolution_values = 0; + + } else { + // for default view datatable, eg HtmlTable + + $view->config->translations['value'] = 'Temperature in °C'; + $view->config->translations['label'] = 'Hour of day'; + $view->requestConfig->filter_sort_column = 'label'; + $view->requestConfig->filter_sort_order = 'asc'; + $view->requestConfig->filter_limit = 24; + $view->config->columns_to_display = array('label', 'value'); + $view->config->y_axis_unit = '°C'; // useful if the user requests the bar graph + $view->config->show_exclude_low_population = false; + $view->config->show_table_all_columns = false; + $view->config->disable_row_evolution = true; + $view->config->max_graph_elements = 24; + $view->config->metrics_documentation = array('value' => 'Documentation for temperature metric'); + } + } + +} diff --git a/plugins/ExampleUI/Reports/GetTemperaturesEvolution.php b/plugins/ExampleUI/Reports/GetTemperaturesEvolution.php new file mode 100644 index 0000000000..ff8ea66e10 --- /dev/null +++ b/plugins/ExampleUI/Reports/GetTemperaturesEvolution.php @@ -0,0 +1,95 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\ExampleUI\Reports; + +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Plugin\Report; +use Piwik\Plugin\ViewDataTable; + +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Bar; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Pie; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\Report\ReportWidgetFactory; +use Piwik\View; +use Piwik\Widget\WidgetsList; + +/** + * This class defines a new report. + * + * See {@link http://developer.piwik.org/api-reference/Piwik/Plugin/Report} for more information. + */ +class GetTemperaturesEvolution extends Base +{ + protected function init() + { + parent::init(); + + $this->name = Piwik::translate('ExampleUI_GetTemperaturesEvolution'); + $this->order = 111; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setSubcategoryId('Sparklines') + ->forceViewDataTable(Sparklines::ID) + ); + + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('ExampleUI_TemperaturesEvolution') + ->setSubcategoryId('Evolution Graph') + ->forceViewDataTable(Evolution::ID) + ->setParameters(array('columns' => array('server1', 'server2'))) + ); + + } + + /** + * Here you can configure how your report should be displayed. For instance whether your report supports a search + * etc. You can also change the default request config. For instance change how many rows are displayed by default. + * + * @param ViewDataTable $view + */ + public function configureView(ViewDataTable $view) + { + if ($view->isViewDataTableId(Sparklines::ID)) { + + /** @var Sparklines $view */ + $view->config->addSparklineMetric(array('server1')); + $view->config->addSparklineMetric(array('server2')); + $view->config->addTranslations(array('server1' => 'Evolution of temperature for server piwik.org')); + $view->config->addTranslations(array('server2' => 'Evolution of temperature for server dev.piwik.org')); + + } elseif ($view->isViewDataTableId(Evolution::ID)) { + + /** @var Evolution $view */ + $selectableColumns = array('server1', 'server2'); + + $columns = Common::getRequestVar('columns', false); + if (!empty($columns)) { + $columns = Piwik::getArrayFromApiParameter($columns); + } + + $columns = array_merge($columns ? $columns : array(), $selectableColumns); + $view->config->columns_to_display = $columns; + + $view->config->addTranslations(array_combine($columns, $columns)); + $view->config->selectable_columns = $selectableColumns; + $view->requestConfig->filter_sort_column = 'label'; + $view->requestConfig->filter_sort_order = 'asc'; + $view->config->documentation = 'My documentation'; + $view->config->show_goals = false; + } + } + +} diff --git a/plugins/ExampleUI/lang/en.json b/plugins/ExampleUI/lang/en.json new file mode 100644 index 0000000000..e0c05c5411 --- /dev/null +++ b/plugins/ExampleUI/lang/en.json @@ -0,0 +1,8 @@ +{ + "ExampleUI": { + "UiFramework": "UI Framework", + "GetTemperaturesDataTable": "Data tables", + "GetTemperaturesEvolution": "Temperatures evolution over time", + "TemperaturesEvolution": "Evolution of server temperatures over the last few days" + } +}
\ No newline at end of file diff --git a/plugins/ExampleUI/plugin.json b/plugins/ExampleUI/plugin.json index ec2fcfe025..8a39e0a2a2 100644 --- a/plugins/ExampleUI/plugin.json +++ b/plugins/ExampleUI/plugin.json @@ -2,14 +2,23 @@ "name": "ExampleUI", "description": "Piwik Platform showcase: how to display data tables, graphs, and the UI framework.", "version": "1.0.1", - "keywords": ["example", "framework", "platform", "ui", "visualization"], - "homepage": "http://piwik.org", + "keywords": [ + "example", + "framework", + "platform", + "ui", + "visualization" + ], + "homepage": "http:\/\/piwik.org", "license": "GPL v3+", "authors": [ { "name": "Piwik", "email": "hello@piwik.org", - "homepage": "http://piwik.org" + "homepage": "http:\/\/piwik.org" } - ] + ], + "require": { + "piwik": ">=2.13.1" + } }
\ No newline at end of file diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php index 80a898fe34..351e700782 100644 --- a/plugins/Goals/API.php +++ b/plugins/Goals/API.php @@ -22,6 +22,7 @@ use Piwik\Plugin\Report; use Piwik\Plugins\API\DataTable\MergeDataTables; use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate; use Piwik\Plugins\Goals\Columns\Metrics\AverageOrderRevenue; +use Piwik\Plugin\Reports; use Piwik\Segment\SegmentExpression; use Piwik\Site; use Piwik\Tracker\Cache; @@ -414,7 +415,7 @@ class API extends \Piwik\Plugin\API $requestedColumns = array_unique(array_merge($requestedColumns, $metricsToAdd)); } - $report = Report::factory('Goals', 'getMetrics'); + $report = Reports::factory('Goals', 'getMetrics'); $columnsToGet = $report->getMetricsRequiredForReport($allMetrics, $requestedColumns); $inDbMetricNames = array_map(function ($name) use ($idGoal) { diff --git a/plugins/Goals/Categories/AddANewGoalSubcategory.php b/plugins/Goals/Categories/AddANewGoalSubcategory.php new file mode 100644 index 0000000000..9b613f2e55 --- /dev/null +++ b/plugins/Goals/Categories/AddANewGoalSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals\Categories; + +use Piwik\Category\Subcategory; + +class AddANewGoalSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Goals'; + protected $id = 'Goals_AddNewGoal'; + protected $order = 9999; + +} diff --git a/plugins/Goals/Categories/GoalsCategory.php b/plugins/Goals/Categories/GoalsCategory.php new file mode 100644 index 0000000000..7ba4b4df58 --- /dev/null +++ b/plugins/Goals/Categories/GoalsCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals\Categories; + +use Piwik\Category\Category; + +class GoalsCategory extends Category +{ + protected $id = 'Goals_Goals'; + protected $order = 25; +} diff --git a/plugins/Goals/Categories/GoalsOverviewSubcategory.php b/plugins/Goals/Categories/GoalsOverviewSubcategory.php new file mode 100644 index 0000000000..95f2d815c9 --- /dev/null +++ b/plugins/Goals/Categories/GoalsOverviewSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals\Categories; + +use Piwik\Category\Subcategory; + +class GoalsOverviewSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Goals'; + protected $id = 'General_Overview'; + protected $order = 2; + +} diff --git a/plugins/Goals/Categories/ManageGoalsSubcategory.php b/plugins/Goals/Categories/ManageGoalsSubcategory.php new file mode 100644 index 0000000000..ef71697981 --- /dev/null +++ b/plugins/Goals/Categories/ManageGoalsSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals\Categories; + +use Piwik\Category\Subcategory; + +class ManageGoalsSubcategory extends Subcategory +{ + protected $categoryId = 'Goals_Goals'; + protected $id = 'Goals_ManageGoals'; + protected $order = 9999; + +} diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php index 15f7b40aab..966005b690 100644 --- a/plugins/Goals/Controller.php +++ b/plugins/Goals/Controller.php @@ -8,16 +8,16 @@ */ namespace Piwik\Plugins\Goals; -use Exception; use Piwik\API\Request; use Piwik\Common; use Piwik\DataTable; +use Piwik\DataTable\Renderer\Json; use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal; +use Piwik\FrontController; use Piwik\Piwik; use Piwik\Plugins\Referrers\API as APIReferrers; use Piwik\Translation\Translator; use Piwik\View; -use Piwik\View\ReportsByDimension; /** * @@ -48,11 +48,6 @@ class Controller extends \Piwik\Plugin\Controller */ private $translator; - /** - * @var TranslationHelper - */ - private $translationHelper; - private function formatConversionRate($conversionRate, $columnName = 'conversion_rate') { if ($conversionRate instanceof DataTable) { @@ -70,89 +65,16 @@ class Controller extends \Piwik\Plugin\Controller return $conversionRate; } - public function __construct(Translator $translator, TranslationHelper $translationHelper) + public function __construct(Translator $translator) { parent::__construct(); $this->translator = $translator; - $this->translationHelper = $translationHelper; $this->idSite = Common::getRequestVar('idSite', null, 'int'); $this->goals = API::getInstance()->getGoals($this->idSite); } - public function widgetGoalReport() - { - $view = $this->getGoalReportView($idGoal = Common::getRequestVar('idGoal', null, 'string')); - $view->displayFullReport = false; - return $view->render(); - } - - public function goalReport() - { - $view = $this->getGoalReportView($idGoal = Common::getRequestVar('idGoal', null, 'string')); - $view->displayFullReport = true; - return $view->render(); - } - - protected function getGoalReportView($idGoal = false) - { - $view = new View('@Goals/getGoalReportView'); - if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { - $goalDefinition['name'] = $this->translator->translate('Goals_Ecommerce'); - $goalDefinition['allow_multiple'] = true; - $ecommerce = $view->ecommerce = true; - } else { - if (!isset($this->goals[$idGoal])) { - Piwik::redirectToModule('Goals', 'index', array('idGoal' => null)); - } - $goalDefinition = $this->goals[$idGoal]; - } - $this->setGeneralVariablesView($view); - $goal = $this->getMetricsForGoal($idGoal); - foreach ($goal as $name => $value) { - $view->$name = $value; - } - if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { - $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART); - foreach ($goal as $name => $value) { - $name = 'cart_' . $name; - $view->$name = $value; - } - } - $view->showHeadline = false; - $view->idGoal = $idGoal; - $view->goalName = $goalDefinition['name']; - $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple']; - $view->graphEvolution = $this->getEvolutionGraph(array(), $idGoal, array('nb_conversions')); - $view->nameGraphEvolution = 'Goals.getEvolutionGraph' . $idGoal; - $view->topDimensions = $this->getTopDimensions($idGoal); - - $goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal)); - - // conversion rate for new and returning visitors - $view->conversion_rate_returning = $this->formatConversionRate($goalMetrics, 'conversion_rate_returning_visit'); - $view->conversion_rate_new = $this->formatConversionRate($goalMetrics, 'conversion_rate_new_visit'); - - $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable( - $view->nb_conversions, isset($ecommerce), !empty($view->cart_nb_conversions)); - return $view; - } - - public function index() - { - $view = $this->getOverviewView(); - - // unsanitize goal names and other text data (not done in API so as not to break - // any other code/cause security issues) - $goals = $this->goals; - $view->goalsJSON = json_encode($goals); - - $view->ecommerceEnabled = $this->site->isEcommerceEnabled(); - $view->displayFullReport = true; - return $view->render(); - } - public function manage() { Piwik::checkUserHasAdminAccess($this->idSite); @@ -164,48 +86,20 @@ class Controller extends \Piwik\Plugin\Controller return $view->render(); } - public function widgetGoalsOverview() + public function goalConversionsOverview() { - $view = $this->getOverviewView(); - $view->displayFullReport = false; - return $view->render(); - } + $view = new View('@Goals/conversionOverview'); + $idGoal = Common::getRequestVar('idGoal', null, 'string'); - protected function getOverviewView() - { - $view = new View('@Goals/getOverviewView'); - $this->setGeneralVariablesView($view); - - $view->graphEvolution = $this->getEvolutionGraph(array(), false, array('nb_conversions')); - $view->nameGraphEvolution = 'GoalsgetEvolutionGraph'; - - // sparkline for the historical data of the above values - $view->urlSparklineConversions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => '')); - $view->urlSparklineConversionRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => '')); - $view->urlSparklineRevenue = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => '')); - - // Pass empty idGoal will return Goal overview - $request = new Request("method=Goals.get&format=original&idGoal="); - $datatable = $request->process(); - $dataRow = $datatable->getFirstRow(); + $view->topDimensions = $this->getTopDimensions($idGoal); - $view->nb_conversions = $dataRow->getColumn('nb_conversions'); - $view->nb_visits_converted = $dataRow->getColumn('nb_visits_converted'); - $view->conversion_rate = $this->formatConversionRate($dataRow->getColumn('conversion_rate')); - $view->revenue = $dataRow->getColumn('revenue'); + $goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal)); - $goalMetrics = array(); - foreach ($this->goals as $idGoal => $goal) { - $goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal); - $goalMetrics[$idGoal]['name'] = $goal['name']; - $goalMetrics[$idGoal]['goalAllowMultipleConversionsPerVisit'] = $goal['allow_multiple']; - } + // conversion rate for new and returning visitors + $view->conversion_rate_returning = $this->formatConversionRate($goalMetrics, 'conversion_rate_returning_visit'); + $view->conversion_rate_new = $this->formatConversionRate($goalMetrics, 'conversion_rate_new_visit'); - $view->goalMetrics = $goalMetrics; - $view->goals = $this->goals; - $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable( - $view->nb_conversions, $ecommerce = false, !empty($view->cart_nb_conversions)); - return $view; + return $view->render(); } public function getLastNbConversionsGraph() @@ -244,6 +138,26 @@ class Controller extends \Piwik\Plugin\Controller return $view->render(); } + public function hasConversions() + { + $this->checkSitePermission(); + + $idGoal = Common::getRequestVar('idGoal', '', 'string'); + $idSite = Common::getRequestVar('idSite', null, 'int'); + $period = Common::getRequestVar('period', null, 'string'); + $date = Common::getRequestVar('date', null, 'string'); + + Piwik::checkUserHasViewAccess($idSite); + + $conversions = new Conversions(); + + Json::sendHeaderJSON(); + + $numConversions = $conversions->getConversionForGoal($idGoal, $idSite, $period, $date); + + return json_encode($numConversions > 0); + } + public function getEvolutionGraph(array $columns = array(), $idGoal = false, array $defaultColumns = array()) { if (empty($columns)) { @@ -334,11 +248,11 @@ class Controller extends \Piwik\Plugin\Controller $topDimensions = array(); foreach ($topDimensionsToLoad as $dimensionName => $apiMethod) { $request = new Request("method=$apiMethod - &format=original - &filter_update_columns_when_show_all_goals=1 - &idGoal=" . AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . " - &filter_sort_order=desc - &filter_sort_column=$columnNbConversions" . + &format=original + &filter_update_columns_when_show_all_goals=1 + &idGoal=" . AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . " + &filter_sort_order=desc + &filter_sort_column=$columnNbConversions" . // select a couple more in case some are not valid (ie. conversions==0 or they are "Keyword not defined") "&filter_limit=" . (self::COUNT_TOP_ROWS_TO_DISPLAY + 2)); $datatable = $request->process(); @@ -402,72 +316,11 @@ class Controller extends \Piwik\Plugin\Controller 'avg_order_revenue' => $aov ? $aov : 0, 'urlSparklinePurchasedProducts' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('items'), 'idGoal' => $idGoal)), 'urlSparklineAverageOrderValue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_order_revenue'), 'idGoal' => $idGoal)), - )); + )); } return $return; } - /** - * Utility function that returns HTML that displays Goal information for reports. This - * is the HTML that is at the bottom of every goals page. - * - * @param int $conversions The number of conversions for this goal (or all goals - * in case of the overview). - * @param bool $ecommerce Whether to show ecommerce reports or not. - * @param bool $cartNbConversions Whether there are cart conversions or not for this - * goal. - * @return string - */ - private function getGoalReportsByDimensionTable($conversions, $ecommerce = false, $cartNbConversions = false) - { - $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0; - - $goalReportsByDimension = new ReportsByDimension('Goals'); - - // add ecommerce reports - $ecommerceCustomParams = array(); - if ($ecommerce) { - if ($preloadAbandonedCart) { - $ecommerceCustomParams['abandonedCarts'] = '1'; - } else { - $ecommerceCustomParams['abandonedCarts'] = '0'; - } - } - - if ($conversions > 0 || $ecommerce) { - // for non-Goals reports, we show the goals table - $customParams = $ecommerceCustomParams + array('documentationForGoalsPage' => '1'); - - if (Common::getRequestVar('idGoal', '') === '') // if no idGoal, use 0 for overview - { - $customParams['idGoal'] = '0'; // NOTE: Must be string! Otherwise Piwik_View_HtmlTable_Goals fails. - } - - $allReports = Goals::getReportsWithGoalMetrics(); - foreach ($allReports as $category => $reports) { - if ($ecommerce) { - $categoryText = $this->translationHelper->translateEcommerceMetricCategory($category); - } else { - $categoryText = $this->translationHelper->translateGoalMetricCategory($category); - } - - foreach ($reports as $report) { - if (empty($report['viewDataTable']) - && empty($report['abandonedCarts']) - ) { - $report['viewDataTable'] = 'tableGoals'; - } - $customParams['viewDataTable'] = $report['viewDataTable']; - - $goalReportsByDimension->addReport( - $categoryText, $report['name'], $report['module'] . '.' . $report['action'], $customParams); - } - } - } - - return $goalReportsByDimension->render(); - } - private function setEditGoalsViewVariables($view) { $goals = $this->goals; @@ -496,4 +349,32 @@ class Controller extends \Piwik\Plugin\Controller { $view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite); } + + /** + * @deprecated used to be a widgetized URL. There to not break widget URLs + */ + public function widgetGoalReport() + { + $idGoal = Common::getRequestVar('idGoal', '', 'string'); + + if ($idGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) { + $_GET['containerId'] = 'EcommerceOverview'; + } elseif (!empty($idGoal)) { + $_GET['containerId'] = 'Goal_' . (int) $idGoal; + } else { + return ''; + } + + return FrontController::getInstance()->fetchDispatch('CoreHome', 'renderWidgetContainer'); + } + + /** + * @deprecated used to be a widgetized URL. There to not break widget URLs + */ + public function widgetGoalsOverview() + { + $_GET['containerId'] = 'GoalsOverview'; + + return FrontController::getInstance()->fetchDispatch('CoreHome', 'renderWidgetContainer'); + } } diff --git a/plugins/Goals/Conversions.php b/plugins/Goals/Conversions.php new file mode 100644 index 0000000000..fbbbef0078 --- /dev/null +++ b/plugins/Goals/Conversions.php @@ -0,0 +1,45 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals; + +use Piwik\API\Request; +use Piwik\Cache; +use Piwik\Common; +use Piwik\Db; + +class Conversions +{ + + public function getConversionForGoal($idGoal, $idSite, $period, $date) + { + if (!$period || !$date || !$idSite) { + return false; + } + + $datatable = Request::processRequest('Goals.get', array( + 'idGoal' => $idGoal, + 'period' => $period, + 'date' => $date, + 'idSite' => $idSite, + 'serialize' => 0, + 'segment' => false + )); + + // we ignore the segment even if there is one set. We still want to show conversion overview if there are conversions + // in general but not for this segment + + $dataRow = $datatable->getFirstRow(); + + if (!$dataRow) { + return false; + } + + return $dataRow->getColumn('nb_conversions'); + } +} diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php index d26e2cf77e..609d98c419 100644 --- a/plugins/Goals/Goals.php +++ b/plugins/Goals/Goals.php @@ -13,8 +13,9 @@ use Piwik\Common; use Piwik\Db; use Piwik\Piwik; use Piwik\Plugin\Report; +use Piwik\Plugin\Reports; use Piwik\Tracker\GoalManager; -use Piwik\Translate; +use Piwik\Category\Subcategory; /** * @@ -29,36 +30,17 @@ class Goals extends \Piwik\Plugin foreach ($dimensions as $dimension) { $group = $dimension['category']; // move "Custom Variables" report to the "Goals/Sales by User attribute" category - if ($dimension['module'] === 'CustomVariables') { + if ($dimension['module'] === 'CustomVariables' + || $dimension['action'] == 'getVisitInformationPerServerTime') { $group = 'VisitsSummary_VisitsSummary'; } unset($dimension['category']); $dimensionsByGroup[$group][] = $dimension; } - uksort($dimensionsByGroup, array('self', 'sortGoalDimensionsByModule')); return $dimensionsByGroup; } - public static function sortGoalDimensionsByModule($a, $b) - { - static $order = null; - - if (is_null($order)) { - $order = array( - 'Referrers_Referrers', - 'General_Visit', - 'General_Visitors', - 'VisitsSummary_VisitsSummary', - 'VisitTime_ColumnServerTime', - ); - } - - $orderA = array_search($a, $order); - $orderB = array_search($b, $order); - return $orderA > $orderB; - } - public static function getGoalColumns($idGoal) { $columns = array( @@ -98,11 +80,34 @@ class Goals extends \Piwik\Plugin 'SitesManager.deleteSite.end' => 'deleteSiteGoals', 'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics', 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', - 'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations' + 'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations', + 'Category.addSubcategories' => 'addSubcategories' ); return $hooks; } + public function addSubcategories(&$subcategories) + { + $idSite = Common::getRequestVar('idSite', 0, 'int'); + + if (!$idSite) { + return; + } + + $goals = API::getInstance()->getGoals($idSite); + + $order = 900; + foreach ($goals as $goal) { + $category = new Subcategory(); + $category->setName($goal['name']); + $category->setCategoryId('Goals_Goals'); + $category->setId($goal['idgoal']); + $category->setOrder($order++); + $subcategories[] = $category; + } + } + + public function addMetricTranslations(&$translations) { $metrics = array( @@ -176,10 +181,12 @@ class Goals extends \Piwik\Plugin { $reportsWithGoals = array(); - foreach (Report::getAllReports() as $report) { + $reports = new Reports(); + + foreach ($reports->getAllReports() as $report) { if ($report->hasGoalMetrics()) { $reportsWithGoals[] = array( - 'category' => $report->getCategoryKey(), + 'category' => $report->getCategoryId(), 'name' => $report->getName(), 'module' => $report->getModule(), 'action' => $report->getAction(), @@ -271,5 +278,6 @@ class Goals extends \Piwik\Plugin $translationKeys[] = 'Goals_DeleteGoalConfirm'; $translationKeys[] = 'Goals_Ecommerce'; $translationKeys[] = 'Goals_Optional'; + $translationKeys[] = 'Goals_ChooseGoal'; } } diff --git a/plugins/Goals/Menu.php b/plugins/Goals/Menu.php index 85db036797..503c7e0430 100644 --- a/plugins/Goals/Menu.php +++ b/plugins/Goals/Menu.php @@ -10,7 +10,6 @@ namespace Piwik\Plugins\Goals; use Piwik\Common; use Piwik\Menu\Group; -use Piwik\Menu\MenuReporting; use Piwik\Menu\MenuUser; use Piwik\Piwik; use Piwik\Plugins\UsersManager\UserPreferences; @@ -18,48 +17,6 @@ use Piwik\Translate; class Menu extends \Piwik\Plugin\Menu { - public function configureReportingMenu(MenuReporting $menu) - { - $idSite = $this->getIdSite(); - $goals = API::getInstance()->getGoals($idSite); - $mainGoalMenu = 'Goals_Goals'; - - if (count($goals) == 0) { - $linkToAddNewGoal = $this->urlForAction('addNewGoal', array( - 'idGoal' => null, - )); - $menu->addItem($mainGoalMenu, '', $linkToAddNewGoal, 25); - $menu->addItem($mainGoalMenu, 'Goals_AddNewGoal', $linkToAddNewGoal, 1); - return; - } - - $order = 1; - - $url = $this->urlForAction('index', array('idGoal' => null)); - - $menu->addItem($mainGoalMenu, '', $url, 25); - $menu->addItem($mainGoalMenu, 'General_Overview', $url, ++$order); - - $group = new Group(); - foreach ($goals as $goal) { - $subMenuName = str_replace('%', '%%', Translate::clean($goal['name'])); - $params = $this->urlForAction('goalReport', array('idGoal' => $goal['idgoal'])); - $tooltip = sprintf('%s (id = %d)', $subMenuName, $goal['idgoal']); - - if (count($goals) > 3) { - $group->add($subMenuName, $params, $tooltip); - } else { - $menu->addItem($mainGoalMenu, $subMenuName, $params, ++$order, $tooltip); - } - } - - if (count($goals) > 3) { - $menu->addGroup($mainGoalMenu, 'Goals_ChooseGoal', $group, ++$order, $tooltip = false); - } - - $menu->addItem($mainGoalMenu, 'Goals_ManageGoals', $this->urlForAction('editGoals'), ++$order); - } - public function configureUserMenu(MenuUser $menu) { $userPreferences = new UserPreferences(); diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php new file mode 100644 index 0000000000..9ab7a96d66 --- /dev/null +++ b/plugins/Goals/Pages.php @@ -0,0 +1,339 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals; + + +use Piwik\API\Request; +use Piwik\Cache; +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Plugin\Report; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\Plugin\Reports; +use Piwik\Widget\WidgetContainerConfig; +use Piwik\Widget\WidgetConfig; +use Piwik\Report\ReportWidgetFactory; + +class Pages +{ + private $orderId = 0; + private $allReports = array(); + private $factory = array(); + private $conversions; + + public function __construct(ReportWidgetFactory $reportFactory, $reportsWithGoalMetrics) + { + $this->factory = $reportFactory; + $this->allReports = $reportsWithGoalMetrics; + $this->conversions = new Conversions(); + } + + /** + * @param array $goals + * @return WidgetConfig[] + */ + public function createGoalsOverviewPage($goals) + { + $subcategory = 'General_Overview'; + + $widgets = array(); + + $config = $this->factory->createWidget(); + $config->forceViewDataTable(Evolution::ID); + $config->setSubcategoryId($subcategory); + $config->setAction('getEvolutionGraph'); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $widgets[] = $config; + + $config = $this->factory->createWidget(); + $config->forceViewDataTable(Sparklines::ID); + $config->setSubcategoryId($subcategory); + $config->setName(''); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $widgets[] = $config; + + foreach ($goals as $goal) { + $name = Common::sanitizeInputValue($goal['name']); + $goalTranslated = Piwik::translate('Goals_GoalX', array($name)); + + $config = $this->factory->createWidget(); + $config->setName($goalTranslated); + $config->setSubcategoryId($subcategory); + $config->forceViewDataTable(Sparklines::ID); + $config->setParameters(array('idGoal' => $goal['idgoal'])); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $config->addParameters(array('allow_multiple' => (int) $goal['allow_multiple'], 'only_summary' => '1')); + $widgets[] = $config; + } + + $container = $this->createWidgetizableWidgetContainer('GoalsOverview', $subcategory, $widgets); + + $config = $this->factory->createContainerWidget('Goals'); + $config->setSubcategoryId($subcategory); + $config->setName('Goals_ConversionsOverviewBy'); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $this->buildGoalByDimensionView('', $config); + $config->setMiddlewareParameters(array( + 'module' => 'Goals', + 'action' => 'hasConversions' + )); + + return array($container, $config); + } + + /** + * @return WidgetConfig[] + */ + public function createEcommerceOverviewPage() + { + $category = 'Goals_Ecommerce'; + $subcategory = 'General_Overview'; + $idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER; + + $widgets = array(); + $config = $this->factory->createWidget(); + $config->forceViewDataTable(Evolution::ID); + $config->setCategoryId($category); + $config->setSubcategoryId($subcategory); + $config->setAction('getEvolutionGraph'); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $config->setParameters(array('idGoal' => $idGoal)); + $widgets[] = $config; + + $config = $this->factory->createWidget(); + $config->setCategoryId($category); + $config->forceViewDataTable(Sparklines::ID); + $config->setSubcategoryId($subcategory); + $config->setName(''); + $config->setModule('Ecommerce'); + $config->setAction('getSparklines'); + $config->setParameters(array('idGoal' => $idGoal)); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $widgets[] = $config; + + $config = $this->factory->createWidget(); + $config->setModule('Ecommerce'); + $config->setAction('getConversionsOverview'); + $config->setSubcategoryId($idGoal); + $config->setName('Goals_ConversionsOverview'); + $config->setParameters(array('idGoal' => $idGoal)); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $config->setMiddlewareParameters(array( + 'module' => 'Goals', + 'action' => 'hasConversions', + 'idGoal' => $idGoal + )); + + $widgets[] = $config; + + $container = $this->createWidgetizableWidgetContainer('EcommerceOverview', $subcategory, $widgets); + return array($container); + } + + /** + * @return WidgetConfig[] + */ + public function createEcommerceSalesPage() + { + $category = 'Goals_Ecommerce'; + $subcategory = 'Ecommerce_Sales'; + + $config = $this->factory->createContainerWidget('GoalsOrder'); + $config->setCategoryId($category); + $config->setSubcategoryId($subcategory); + $config->setName(''); + $config->setParameters(array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER)); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $this->buildGoalByDimensionView(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER, $config); + + return array($config); + } + + /** + * @param array $goal + * @return WidgetConfig[] + */ + public function createGoalDetailPage($goal) + { + $widgets = array(); + + $idGoal = (int) $goal['idgoal']; + $name = Common::sanitizeInputValue($goal['name']); + $params = array('idGoal' => $idGoal); + + $config = $this->factory->createWidget(); + $config->setSubcategoryId($idGoal); + $config->forceViewDataTable(Evolution::ID); + $config->setAction('getEvolutionGraph'); + $config->setParameters($params); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $widgets[] = $config; + + $config = $this->factory->createWidget(); + $config->setSubcategoryId($idGoal); + $config->setName(''); + $config->forceViewDataTable(Sparklines::ID); + $config->setParameters($params); + $config->addParameters(array('allow_multiple' => (int) $goal['allow_multiple'])); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $config->setIsNotWidgetizable(); + $widgets[] = $config; + + $config = $this->factory->createWidget(); + $config->setAction('goalConversionsOverview'); + $config->setSubcategoryId($idGoal); + $config->setName('Goals_ConversionsOverview'); + $config->setParameters($params); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $config->setMiddlewareParameters(array( + 'module' => 'Goals', + 'action' => 'hasConversions', + 'idGoal' => $idGoal + )); + $widgets[] = $config; + + $container = $this->createWidgetizableWidgetContainer('Goal_' . $idGoal, $name, $widgets); + + $configs = array($container); + + $config = $this->factory->createContainerWidget('Goals' . $idGoal); + $config->setName(Piwik::translate('Goals_GoalConversionsBy', array($name))); + $config->setSubcategoryId($idGoal); + $config->setParameters(array()); + $config->setOrder(++$this->orderId); + $config->setIsNotWidgetizable(); + $config->setMiddlewareParameters(array( + 'module' => 'Goals', + 'action' => 'hasConversions', + 'idGoal' => $idGoal + )); + $this->buildGoalByDimensionView($idGoal, $config); + + $configs[] = $config; + + return $configs; + } + + private function createWidgetizableWidgetContainer($containerId, $pageName, $widgets) + { + /** @var \Piwik\Widget\WidgetConfig[] $widgets */ + $firstWidget = reset($widgets); + /** @var \Piwik\Report\ReportWidgetConfig $firstWidget */ + + if (!empty($pageName)) { + // make sure to not show two titles (one for this container and one for the first widget) + $firstWidget->setName(''); + } + + $config = $this->factory->createContainerWidget($containerId); + $config->setName($pageName); + $config->setCategoryId($firstWidget->getCategoryId()); + $config->setSubcategoryId($firstWidget->getSubcategoryId()); + $config->setIsWidgetizable(); + $config->setOrder($this->orderId++); + + foreach ($widgets as $widget) { + $config->addWidgetConfig($widget); + } + + return $config; + } + + private function buildGoalByDimensionView($idGoal, WidgetContainerConfig $container) + { + $container->setLayout('ByDimension'); + $ecommerce = ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER); + + // for non-Goals reports, we show the goals table + $customParams = array('documentationForGoalsPage' => '1'); + + if ($idGoal === '') { + // if no idGoal, use 0 for overview. Must be string! Otherwise Piwik_View_HtmlTable_Goals fails. + $customParams['idGoal'] = '0'; + } else { + $customParams['idGoal'] = $idGoal; + } + + $translationHelper = new TranslationHelper(); + + foreach ($this->allReports as $category => $reports) { + $order = ($this->getSortOrderOfCategory($category) * 100); + + if ($ecommerce) { + $categoryText = $translationHelper->translateEcommerceMetricCategory($category); + } else { + $categoryText = $translationHelper->translateGoalMetricCategory($category); + } + + foreach ($reports as $report) { + $order++; + + if (empty($report['viewDataTable']) + && empty($report['abandonedCarts']) + ) { + $report['viewDataTable'] = 'tableGoals'; + } + + $widget = $this->createWidgetForReport($report['module'], $report['action']); + $widget->setParameters($customParams); + $widget->setCategoryId($categoryText); + $widget->setSubcategoryId($categoryText); + $widget->setOrder($order); + $widget->setIsNotWidgetizable(); + + if (!empty($report['viewDataTable'])) { + $widget->setDefaultViewDataTable($report['viewDataTable']); + } + + $container->addWidgetConfig($widget); + } + } + } + + private function getSortOrderOfCategory($category) + { + static $order = null; + + if (is_null($order)) { + $order = array( + 'Referrers_Referrers', + 'General_Visit', + 'General_Visitors', + 'VisitsSummary_VisitsSummary', + ); + } + + $value = array_search($category, $order); + + if (false === $value) { + $value = count($order) + 1; + } + + return $value; + } + + private function createWidgetForReport($module, $action) + { + $factory = new ReportWidgetFactory(Reports::factory($module, $action)); + return $factory->createWidget(); + } + +} diff --git a/plugins/Goals/Reports/Base.php b/plugins/Goals/Reports/Base.php index fd732035d3..3f9cb9c98a 100644 --- a/plugins/Goals/Reports/Base.php +++ b/plugins/Goals/Reports/Base.php @@ -18,7 +18,7 @@ abstract class Base extends \Piwik\Plugin\Report protected function init() { - $this->category = 'Goals_Goals'; + $this->categoryId = 'Goals_Goals'; } protected function addReportMetadataForEachGoal(&$availableReports, $infos, $goalNameFormatter) diff --git a/plugins/Goals/Reports/Get.php b/plugins/Goals/Reports/Get.php index bccecda146..66f167fb24 100644 --- a/plugins/Goals/Reports/Get.php +++ b/plugins/Goals/Reports/Get.php @@ -8,7 +8,20 @@ */ namespace Piwik\Plugins\Goals\Reports; +use Piwik\Common; +use Piwik\DataTable; +use Piwik\Metrics\Formatter; use Piwik\Piwik; +use Piwik\Plugin; +use Piwik\Plugin\ViewDataTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\Plugins\Goals\API; +use Piwik\Plugins\Goals\Goals; +use Piwik\Plugins\Goals\Pages; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Site; +use Piwik\Widget\WidgetsList; class Get extends Base { @@ -25,6 +38,104 @@ class Get extends Base $this->parameters = null; } + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $idSite = $this->getIdSite(); + $goals = API::getInstance()->getGoals($idSite); + $reports = Goals::getReportsWithGoalMetrics(); + + $page = new Pages($factory, $reports); + + $widgetsList->addWidgetConfigs($page->createGoalsOverviewPage($goals)); + + if ($this->isEcommerceEnabled($idSite)) { + $widgetsList->addWidgetConfigs($page->createEcommerceOverviewPage()); + $widgetsList->addWidgetConfigs($page->createEcommerceSalesPage()); + } + + foreach ($goals as $goal) { + $widgetsList->addWidgetConfigs($page->createGoalDetailPage($goal)); + } + } + + private function getIdSite() + { + return Common::getRequestVar('idSite', null, 'int'); + } + + private function isEcommerceEnabled($idSite) + { + if (!Plugin\Manager::getInstance()->isPluginActivated('Ecommerce')) { + return false; + } + + $site = new Site($idSite); + return $site->isEcommerceEnabled(); + } + + public function configureView(ViewDataTable $view) + { + if ($view->isViewDataTableId(Sparklines::ID)) { + /** @var Sparklines $view */ + $idSite = $this->getIdSite(); + $isEcommerceEnabled = $this->isEcommerceEnabled($idSite); + + $idGoal = Common::getRequestVar('idGoal', 0, 'int'); + + $formatter = new Formatter(); + $view->config->filters[] = function (DataTable $table) use ($formatter, $idSite) { + $firstRow = $table->getFirstRow(); + if ($firstRow) { + $revenue = $firstRow->getColumn('revenue'); + $firstRow->setColumn('revenue', $formatter->getPrettyMoney($revenue, $idSite)); + } + }; + + $view->config->addTranslations(array( + 'nb_visits' => Piwik::translate('VisitsSummary_NbVisitsDescription'), + 'nb_conversions' => Piwik::translate('Goals_ConversionsDescription'), + 'nb_visits_converted' => Piwik::translate('General_NVisits'), + 'conversion_rate' => Piwik::translate('Goals_OverallConversionRate'), + 'revenue' => Piwik::translate('Goals_OverallRevenue'), + )); + + $allowMultiple = Common::getRequestVar('allow_multiple', 0, 'int'); + + if ($allowMultiple) { + $view->config->addSparklineMetric(array('nb_conversions', 'nb_visits_converted'), $order = 10); + } else { + $view->config->addSparklineMetric(array('nb_conversions'), $order = 10); + } + + $view->config->addSparklineMetric(array('conversion_rate'), $order = 20); + + if (empty($idGoal)) { + // goals overview sparklines below evolution graph + + if ($isEcommerceEnabled) { + // this would be ideally done in Ecommerce plugin but then it is hard to keep same order + $view->config->addSparklineMetric(array('revenue'), $order = 30); + } + + } else { + $onlySummary = Common::getRequestVar('only_summary', 0, 'int'); + + if ($onlySummary) { + // in Goals Overview we list an overview for each goal.... + $view->config->addTranslation('conversion_rate', Piwik::translate('Goals_ConversionRate')); + + } elseif ($isEcommerceEnabled) { + // in Goals detail page... + $view->config->addSparklineMetric(array('revenue'), $order = 30); + } + } + } else if ($view->isViewDataTableId(Evolution::ID)) { + if (empty($view->config->columns_to_display)) { + $view->config->columns_to_display = array('nb_conversions'); + } + } + } + public function configureReportMetadata(&$availableReports, $infos) { if (!$this->isEnabled()) { diff --git a/plugins/Goals/Widgets.php b/plugins/Goals/Widgets.php deleted file mode 100644 index 94d91bda5c..0000000000 --- a/plugins/Goals/Widgets.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Goals; - -use Piwik\Common; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'Goals_Goals'; - - protected function init() - { - $this->addWidget('Goals_GoalsOverview', 'widgetGoalsOverview'); - - $idSite = $this->getIdSite(); - $goals = API::getInstance()->getGoals($idSite); - - if (count($goals) > 0) { - foreach ($goals as $goal) { - $name = Common::sanitizeInputValue($goal['name']); - $params = array('idGoal' => $goal['idgoal']); - - $this->addWidget($name, 'widgetGoalReport', $params); - } - } - } - - private function getIdSite() - { - return Common::getRequestVar('idSite', null, 'int'); - } - -} diff --git a/plugins/Goals/Widgets/AddNewGoal.php b/plugins/Goals/Widgets/AddNewGoal.php new file mode 100644 index 0000000000..b692309b9d --- /dev/null +++ b/plugins/Goals/Widgets/AddNewGoal.php @@ -0,0 +1,38 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals\Widgets; + +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Plugins\Goals\API; +use Piwik\Widget\WidgetConfig; + +class AddNewGoal extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $idSite = Common::getRequestVar('idSite', null, 'int'); + $goals = API::getInstance()->getGoals($idSite); + + $config->setCategoryId('Goals_Goals'); + $config->setSubcategoryId('Goals_AddNewGoal'); + $config->setParameters(array('idGoal' => '')); + $config->setIsNotWidgetizable(); + + if (Piwik::isUserHasAdminAccess($idSite)) { + $config->setName('Goals_AddNewGoal'); + } else { + $config->setName('Goals_CreateNewGOal'); + } + + if (count($goals) !== 0) { + $config->disable(); + } + } +} diff --git a/plugins/Goals/Widgets/EditGoals.php b/plugins/Goals/Widgets/EditGoals.php new file mode 100644 index 0000000000..d0bb52667c --- /dev/null +++ b/plugins/Goals/Widgets/EditGoals.php @@ -0,0 +1,37 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Goals\Widgets; + +use Piwik\Common; +use Piwik\Piwik; +use Piwik\Plugins\Goals\API; +use Piwik\Widget\WidgetConfig; + +class EditGoals extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $idSite = Common::getRequestVar('idSite', null, 'int'); + $goals = API::getInstance()->getGoals($idSite); + + $config->setCategoryId('Goals_Goals'); + $config->setSubcategoryId('Goals_ManageGoals'); + $config->setIsNotWidgetizable(); + + if (Piwik::isUserHasAdminAccess($idSite)) { + $config->setName('Goals_ManageGoals'); + } else { + $config->setName('Goals_CreateNewGOal'); + } + + if (count($goals) === 0) { + $config->disable(); + } + } +} diff --git a/plugins/Goals/lang/en.json b/plugins/Goals/lang/en.json index 0545d0640f..8820b333e7 100644 --- a/plugins/Goals/lang/en.json +++ b/plugins/Goals/lang/en.json @@ -36,6 +36,7 @@ "ConversionByTypeReportDocumentation": "This report provides detailed information about the goal performance (conversions, conversion rates and revenue per visit) for each of the categories available in the left panel. %s Please click on one of the categories to view the report. %s For more information, read the %sTracking Goals documentation%s", "ConversionRate": "%s conversion rate", "Conversions": "%s conversions", + "ConversionsDescription": "conversions", "ConversionsOverview": "Conversions Overview", "ConversionsOverviewBy": "Conversions overview by type of visit", "DaysToConv": "Days to Conversion", @@ -79,8 +80,8 @@ "NoGoalsNeedAccess": "Only an Administrator or a user with Super User access can manage Goals for a given website. Please ask your Piwik administrator to set up a Goal for your website. <br>Tracking Goals is a great way to help understand and maximize your website performance!", "NeedAccess": "Only an Administrator or a user with Super User access can manage Goals for a given website.", "Optional": "(optional)", - "OverallConversionRate": "%s overall conversion rate (visits with a completed goal)", - "OverallRevenue": "%s overall revenue", + "OverallConversionRate": "overall conversion rate (visits with a completed goal)", + "OverallRevenue": "overall revenue", "PageTitle": "Page Title", "Pattern": "Pattern", "PluginDescription": "Create Goals and see detailed reports about your goal conversions: evolution over time, revenue per visit, conversions per referrer, per keyword, and more.", diff --git a/plugins/Goals/templates/_titleAndEvolutionGraph.twig b/plugins/Goals/templates/_titleAndEvolutionGraph.twig deleted file mode 100644 index 0a2fdc1e31..0000000000 --- a/plugins/Goals/templates/_titleAndEvolutionGraph.twig +++ /dev/null @@ -1,86 +0,0 @@ -<span data-graph-id="{{ nameGraphEvolution }}"></span> - -{% if displayFullReport or headline is defined %} - <h2 piwik-enriched-headline - {% if idGoal is defined and idGoal and goalName is defined and not ecommerce is defined %} - edit-url="{{ linkTo({'module': 'Goals', 'action': 'manage', 'idGoal': idGoal})|e('html_attr') }}" - feature-name="{{ 'Goals_Details'|translate|e('html_attr') }}" - {% endif %} - >{% if headline is defined %}{{ headline }}{% elseif goalName is defined %}{{ 'Goals_GoalX'|translate(goalName) }}{% else %}{{ 'General_EvolutionOverPeriod'|translate }}{% endif %}</h2> -{% endif %} -{{ graphEvolution|raw }} - -<div id='leftcolumn' style="clear:both;{% if not isWidget and ecommerce is defined %}width:33%;'{% endif %}"> - <div class="sparkline">{{ sparkline(urlSparklineConversions) }} - {% if ecommerce is defined %} - <strong>{{ nb_conversions }}</strong> - {{ 'General_EcommerceOrders'|translate }} - <img src='plugins/Morpheus/images/ecommerceOrder.gif'> - {% else %} - {{ 'Goals_Conversions'|translate("<strong>"~nb_conversions~"</strong>")|raw }} - {% endif %} - {% if goalAllowMultipleConversionsPerVisit is defined and goalAllowMultipleConversionsPerVisit %} - ({{ 'General_NVisits'|translate("<strong>"~nb_visits_converted~"</strong>")|raw }}) - {% endif %} - </div> - {% if revenue != 0 or ecommerce is defined %} - <div class="sparkline"> - {{ sparkline(urlSparklineRevenue) }} - {% set revenue=revenue|money(idSite) %} - {% if ecommerce is defined %} - <strong>{{ revenue|raw }}</strong> {{ 'General_TotalRevenue'|translate }} - {% else %} - {{ 'Goals_OverallRevenue'|translate("<strong>"~revenue~"</strong>")|raw }} - {% endif %} - </div> - {% endif %} - {% if ecommerce is defined %} - <div class="sparkline">{{ sparkline(urlSparklineAverageOrderValue) }} - <strong>{{ avg_order_revenue|money(idSite)|raw }}</strong> - {{ 'General_AverageOrderValue'|translate }} - </div> - {% endif %} - -</div> -<div id='leftcolumn' {% if not isWidget and ecommerce is defined %}style='width:33%;'{% endif %}> - <div class="sparkline">{{ sparkline(urlSparklineConversionRate) }} - {% if ecommerce is defined %} - {% set ecommerceOrdersText %}{{ 'General_EcommerceOrders'|translate }}{% endset %} - {{ 'Goals_ConversionRate'|translate("<strong>"~conversion_rate~"</strong> "~ecommerceOrdersText)|raw }} - {% else %} - {{ 'Goals_OverallConversionRate'|translate("<strong>"~conversion_rate~"</strong>")|raw }} - {% endif %} - </div> - {% if ecommerce is defined %} - <div class="sparkline">{{ sparkline(urlSparklinePurchasedProducts) }} - <strong>{{ items }}</strong> {{ 'General_PurchasedProducts'|translate }}</div> - {% endif %} -</div> -{% if ecommerce is defined %} - <div id='rightcolumn' {% if not isWidget %}style='width:30%;'{% endif %}> - <div> - <img src='plugins/Morpheus/images/ecommerceAbandonedCart.gif'> <em>{{ 'General_AbandonedCarts'|translate }}</em> - </div> - - <div class="sparkline"> - {{ sparkline(cart_urlSparklineConversions) }} - {% set ecommerceAbandonedCartsText %}{{ 'Goals_AbandonedCart'|translate }}{% endset %} - <strong>{{ cart_nb_conversions }}</strong> {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }} - </div> - - <div class="sparkline"> - {{ sparkline(cart_urlSparklineRevenue) }} - {% set revenue %}{{ cart_revenue|money(idSite)|raw }}{% endset %} - {% set revenueText %}{{ 'General_ColumnRevenue'|translate }}{% endset %} - <strong>{{ revenue }}</strong> {{ 'Goals_LeftInCart'|translate(revenueText) }} - </div> - - <div class="sparkline"> - {{ sparkline(cart_urlSparklineConversionRate) }} - <strong>{{ cart_conversion_rate }}</strong> - {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }} - </div> - </div> -{% endif %} -{% include "_sparklineFooter.twig" %} - diff --git a/plugins/Goals/templates/addNewGoal.twig b/plugins/Goals/templates/addNewGoal.twig index a2b44b88e9..844892ecbc 100644 --- a/plugins/Goals/templates/addNewGoal.twig +++ b/plugins/Goals/templates/addNewGoal.twig @@ -1,5 +1,4 @@ {% if userCanEditGoals %} - <h2 piwik-enriched-headline>{{ 'Goals_AddNewGoal'|translate }}</h2> <p>{{ 'Goals_NewGoalIntro'|translate }}</p> <p>{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }} {{ 'Goals_ManageGoalsOrCreateANewGoal'|translate("<a href='#module=Goals&action=editGoals'>","</a>")|raw }} diff --git a/plugins/Goals/templates/conversionOverview.twig b/plugins/Goals/templates/conversionOverview.twig new file mode 100644 index 0000000000..2697527eb7 --- /dev/null +++ b/plugins/Goals/templates/conversionOverview.twig @@ -0,0 +1,15 @@ +<ul class="ulGoalTopElements"> + {% if topDimensions.country is defined %} + <li>{{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}</li> + {% endif %} + {% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %} + <li>{{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}</li> + {% endif %} + {% if topDimensions.website is defined and topDimensions.website|length > 0 %} + <li>{{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}</li> + {% endif %} + <li> + {{ 'Goals_ReturningVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_returning~"</strong>")|raw }} + , {{ 'Goals_NewVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_new~"</strong>")|raw }} + </li> +</ul><br style="clear:left"/>
\ No newline at end of file diff --git a/plugins/Goals/templates/editGoals.twig b/plugins/Goals/templates/editGoals.twig index 76b64b9918..1bc8956511 100644 --- a/plugins/Goals/templates/editGoals.twig +++ b/plugins/Goals/templates/editGoals.twig @@ -1,7 +1,5 @@ {% if userCanEditGoals %} - <h2 piwik-enriched-headline>{{ 'Goals_ManageGoals'|translate }}</h2> - {% include "@Goals/_addEditGoal.twig" %} {% else %} diff --git a/plugins/Goals/templates/getGoalReportView.twig b/plugins/Goals/templates/getGoalReportView.twig deleted file mode 100644 index 2cb3b7f4d0..0000000000 --- a/plugins/Goals/templates/getGoalReportView.twig +++ /dev/null @@ -1,66 +0,0 @@ -<link rel="stylesheet" type="text/css" href="plugins/Goals/stylesheets/goals.css"/> -{% include "@Goals/_titleAndEvolutionGraph.twig" | raw %} - -<div class="clear"></div> -{% if nb_conversions > 0 %} - <h2>{{ 'Goals_ConversionsOverview'|translate }}</h2> - <ul class="ulGoalTopElements"> - {% if ecommerce is not defined %} - {% if topDimensions.country is defined %} - <li>{{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}</li> - {% endif %} - {% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %} - <li>{{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}</li> - {% endif %} - {% if topDimensions.website is defined and topDimensions.website|length > 0 %} - <li>{{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}</li> - {% endif %} - <li> - {{ 'Goals_ReturningVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_returning~"</strong>")|raw }} - , {{ 'Goals_NewVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_new~"</strong>")|raw }} - </li> - {% else %} - <li> - {{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}} - {% if revenue_subtotal is not empty %}, - {{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}} - {% endif %} - {%- if revenue_tax is not empty -%}, - {{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}} - {% endif %} - {%- if revenue_shipping is not empty -%}, - {{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}} - {% endif %} - {%- if revenue_discount is not empty -%}, - {{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}} - {% endif %} - </li> - {% endif %} - </ul> -{% endif %} - -<script type="text/javascript"> - $(document).ready(function () { - $('.goalTopElement').tooltip({ - track: true, - content: function () { - return $(this).attr("title"); - }, - show: false, - hide: false - }); - }); -</script> - -{% if displayFullReport %} - {% if nb_conversions > 0 or cart_nb_conversions is defined %} - <h2 id='titleGoalsByDimension'> - {% if idGoal is defined %} - {{ 'Goals_GoalConversionsBy'|translate(goalName) }} - {% else %} - {{ 'Goals_ConversionsOverviewBy'|translate }} - {% endif %} - </h2> - {{ goalReportsByDimension|raw }} - {% endif %} -{% endif %} diff --git a/plugins/Goals/templates/getOverviewView.twig b/plugins/Goals/templates/getOverviewView.twig deleted file mode 100644 index 909fd01711..0000000000 --- a/plugins/Goals/templates/getOverviewView.twig +++ /dev/null @@ -1,58 +0,0 @@ -<link rel="stylesheet" type="text/css" href="plugins/Goals/stylesheets/goals.css"/> - -{% include "@Goals/_titleAndEvolutionGraph.twig" %} -{% set sum_nb_conversions=nb_conversions %} - -{% for goal in goalMetrics %} - {% set nb_conversions=goal.nb_conversions %} - {% set nb_visits_converted=goal.nb_visits_converted %} - {% set conversion_rate=goal.conversion_rate %} - {% set name=goal.name %} - <div class="goalEntry" style="clear:both"> - <h2> - <a href="javascript:broadcast.propagateAjax('module=Goals&action=goalReport&idGoal={{ goal.id }}')"> - {{ 'Goals_GoalX'|translate("'"~name~"'") }} - </a> - </h2> - - {% if not isWidget %} - <div class="row"> - <div class="col-md-6"> - {% endif %} - - <div class="sparkline">{{ sparkline(goal.urlSparklineConversions) }} - {{ 'Goals_Conversions'|translate("<strong>"~nb_conversions~"</strong>")|raw }} - {% if goal.goalAllowMultipleConversionsPerVisit %} - ({{ 'General_NVisits'|translate("<strong>"~nb_visits_converted~"</strong>") | raw }}) - {% endif %} - </div> - - {% if not isWidget %} - </div> - <div class="col-md-6"> - {% endif %} - - <div class="sparkline">{{ sparkline(goal.urlSparklineConversionRate) }} - {{ 'Goals_ConversionRate'|translate("<strong>"~conversion_rate~"</strong>")|raw }} - </div> - - {% if not isWidget %} - </div> - </div> - {% endif %} - - </div> -{% endfor %} - -{% if displayFullReport %} - {% if sum_nb_conversions != 0 %} - <h2 id='titleGoalsByDimension'> - {% if idGoal is defined %} - {{ 'Goals_GoalConversionsBy'|translate(goalName) }} - {% else %} - {{ 'Goals_ConversionsOverviewBy'|translate }} - {% endif %} - </h2> - {{ goalReportsByDimension|raw }} - {% endif %} -{% endif %} diff --git a/plugins/Insights/Widgets.php b/plugins/Insights/Widgets.php deleted file mode 100644 index 799662deb5..0000000000 --- a/plugins/Insights/Widgets.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Insights; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'Insights_WidgetCategory'; - - public function init() - { - $this->addWidget('Insights_OverviewWidgetTitle', 'getInsightsOverview'); - $this->addWidget('Insights_MoversAndShakersWidgetTitle', 'getOverallMoversAndShakers'); - } -} diff --git a/plugins/Insights/Widgets/GetInsightsOverview.php b/plugins/Insights/Widgets/GetInsightsOverview.php new file mode 100644 index 0000000000..e715865a41 --- /dev/null +++ b/plugins/Insights/Widgets/GetInsightsOverview.php @@ -0,0 +1,20 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Insights\Widgets; + +use Piwik\Widget\WidgetConfig; + +class GetInsightsOverview extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Insights_WidgetCategory'); + $config->setName('Insights_OverviewWidgetTitle'); + } +} diff --git a/plugins/Insights/Widgets/GetOverallMoversAndShakers.php b/plugins/Insights/Widgets/GetOverallMoversAndShakers.php new file mode 100644 index 0000000000..e175e76519 --- /dev/null +++ b/plugins/Insights/Widgets/GetOverallMoversAndShakers.php @@ -0,0 +1,20 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Insights\Widgets; + +use Piwik\Widget\WidgetConfig; + +class GetOverallMoversAndShakers extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Insights_WidgetCategory'); + $config->setName('Insights_MoversAndShakersWidgetTitle'); + } +} diff --git a/plugins/Live/Categories/LiveCategory.php b/plugins/Live/Categories/LiveCategory.php new file mode 100644 index 0000000000..c3b1a52c91 --- /dev/null +++ b/plugins/Live/Categories/LiveCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Live\Categories; + +use Piwik\Category\Category; + +class LiveCategory extends Category +{ + protected $id = 'Live!'; + protected $order = 2; +} diff --git a/plugins/Live/Categories/VisitorLogSubcategory.php b/plugins/Live/Categories/VisitorLogSubcategory.php new file mode 100644 index 0000000000..2ed65cc4ed --- /dev/null +++ b/plugins/Live/Categories/VisitorLogSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Live\Categories; + +use Piwik\Category\Subcategory; + +class VisitorLogSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'Live_VisitorLog'; + protected $order = 5; + +} diff --git a/plugins/Live/Reports/Base.php b/plugins/Live/Reports/Base.php index e64ed55f96..3a9a00945a 100644 --- a/plugins/Live/Reports/Base.php +++ b/plugins/Live/Reports/Base.php @@ -12,7 +12,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'Live!'; + $this->categoryId = 'Live!'; } public function configureReportMetadata(&$availableReports, $infos) diff --git a/plugins/Live/Reports/GetLastVisitsDetails.php b/plugins/Live/Reports/GetLastVisitsDetails.php index 8cf2268d28..f714989b75 100644 --- a/plugins/Live/Reports/GetLastVisitsDetails.php +++ b/plugins/Live/Reports/GetLastVisitsDetails.php @@ -8,10 +8,10 @@ */ namespace Piwik\Plugins\Live\Reports; -use Piwik\Menu\MenuReporting; use Piwik\Plugin\Report; use Piwik\Plugins\Live\Visualizations\VisitorLog; -use Piwik\WidgetsList; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetLastVisitsDetails extends Base { @@ -20,8 +20,9 @@ class GetLastVisitsDetails extends Base protected function init() { parent::init(); - $this->widgetTitle = 'Live_VisitorLog'; $this->order = 2; + $this->categoryId = 'General_Visitors'; + $this->subcategoryId = 'Live_VisitorLog'; } public function getDefaultTypeViewDataTable() @@ -34,17 +35,14 @@ class GetLastVisitsDetails extends Base return true; } - public function configureReportingMenu(MenuReporting $menu) + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) { - if ($this->isEnabled()) { - $url = array('module' => $this->module, 'action' => 'indexVisitorLog'); - $menu->addVisitorsItem($this->widgetTitle, $url, $order = 5); - } - } - - public function configureWidget(WidgetsList $widget) - { - $widget->add($this->category, $this->widgetTitle, $this->module, 'getVisitorLog', array('small' => 1)); + $widget = $factory->createWidget() + ->forceViewDataTable(VisitorLog::ID) + ->setName('Live_VisitorLog') + ->setOrder(10) + ->setParameters(array('small' => 1)); + $widgetsList->addWidgetConfig($widget); } } diff --git a/plugins/Live/Reports/GetSimpleLastVisitCount.php b/plugins/Live/Reports/GetSimpleLastVisitCount.php index 9b0db30769..d2f535ff4e 100644 --- a/plugins/Live/Reports/GetSimpleLastVisitCount.php +++ b/plugins/Live/Reports/GetSimpleLastVisitCount.php @@ -14,17 +14,24 @@ use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugins\Live\Controller; use Piwik\API\Request; +use Piwik\Report\ReportWidgetFactory; use Piwik\View; +use Piwik\Widget\WidgetsList; class GetSimpleLastVisitCount extends Base { protected function init() { parent::init(); - $this->widgetTitle = 'Live_RealTimeVisitorCount'; $this->order = 3; } + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widget = $factory->createWidget()->setName('Live_RealTimeVisitorCount')->setOrder(15); + $widgetsList->addWidgetConfig($widget); + } + public function render() { $lastMinutes = Config::getInstance()->General[Controller::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY]; diff --git a/plugins/Live/Widgets.php b/plugins/Live/Widgets.php deleted file mode 100644 index c4c96c53e9..0000000000 --- a/plugins/Live/Widgets.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Live; - -use Piwik\Piwik; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'Live!'; - - public function init() - { - $this->addWidget('Live_VisitorsInRealTime', 'widget'); - - // the visitor profile uses a segment that is not accessible to the anonymous user, so don't bother showing this widget - if (!Piwik::isUserIsAnonymous()) { - $this->addWidget('Live_VisitorProfile', 'getVisitorProfilePopup'); - } - } - -} diff --git a/plugins/Live/Widgets/GetVisitorProfilePopup.php b/plugins/Live/Widgets/GetVisitorProfilePopup.php new file mode 100644 index 0000000000..0d01a7abca --- /dev/null +++ b/plugins/Live/Widgets/GetVisitorProfilePopup.php @@ -0,0 +1,33 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Live\Widgets; + +use Piwik\Piwik; +use Piwik\Widget\WidgetConfig; + +class GetVisitorProfilePopup extends \Piwik\Widget\Widget +{ + + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('General_Visitors'); + $config->setName('Live_VisitorProfile'); + $config->setOrder(25); + + if (Piwik::isUserIsAnonymous()) { + $config->disable(); + } + } + + public function render() + { + + } + +} diff --git a/plugins/Live/Widgets/Widget.php b/plugins/Live/Widgets/Widget.php new file mode 100644 index 0000000000..e058423b66 --- /dev/null +++ b/plugins/Live/Widgets/Widget.php @@ -0,0 +1,21 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Live\Widgets; + +use Piwik\Widget\WidgetConfig; + +class Widget extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('Live!'); + $config->setName('Live_VisitorsInRealTime'); + $config->setOrder(20); + } +} diff --git a/plugins/Live/templates/index.twig b/plugins/Live/templates/index.twig index ced0699bb9..b0a9382b65 100644 --- a/plugins/Live/templates/index.twig +++ b/plugins/Live/templates/index.twig @@ -43,7 +43,7 @@ </a> {% if not disableLink %} - <a class="rightLink" href="javascript:broadcast.propagateAjax('module=Live&action=getVisitorLog')">{{ 'Live_LinkVisitorLog'|translate }}</a> + <a class="rightLink" href="#" onclick="this.href=broadcast.buildReportingUrl('category=General_Visitors&subcategory=Live_VisitorLog')">{{ 'Live_LinkVisitorLog'|translate }}</a> {% endif %} </div> {% endspaceless %} diff --git a/plugins/Morpheus/javascripts/piwikHelper.js b/plugins/Morpheus/javascripts/piwikHelper.js index 163ea90160..cc6ae5dde5 100644 --- a/plugins/Morpheus/javascripts/piwikHelper.js +++ b/plugins/Morpheus/javascripts/piwikHelper.js @@ -125,6 +125,10 @@ var piwikHelper = { compileAngularComponents: function (selector) { var $element = $(selector); + if (!$element || !$element.length) { + return; + } + angular.element(document).injector().invoke(function($compile) { var scope = angular.element($element).scope(); $compile($element)(scope); @@ -155,9 +159,9 @@ var piwikHelper = { var button = {text: text}; if(typeof handles[role] == 'function') { - button.click = function(){$(this).dialog("close"); handles[role].apply()}; + button.click = function(){ $(this).dialog("close"); handles[role].apply()}; } else { - button.click = function(){$(this).dialog("close");}; + button.click = function(){ $(this).dialog("close");}; } if (title) { diff --git a/plugins/Morpheus/stylesheets/ui/_map.less b/plugins/Morpheus/stylesheets/ui/_map.less index 0446610c2d..731f4e369b 100644 --- a/plugins/Morpheus/stylesheets/ui/_map.less +++ b/plugins/Morpheus/stylesheets/ui/_map.less @@ -56,6 +56,10 @@ font-size: 14px; } +.uiTest .realTimeMap_datetime { + visibility: hidden; +} + .realtime-map[data-name=white-fill] { color: #f2f2f2 !important; } diff --git a/plugins/Morpheus/templates/layout.twig b/plugins/Morpheus/templates/layout.twig index 1e9f1f0d5c..87cb863356 100644 --- a/plugins/Morpheus/templates/layout.twig +++ b/plugins/Morpheus/templates/layout.twig @@ -47,6 +47,8 @@ {% endblock %} </div> + <div piwik-popover-handler></div> + {% endblock %} </body> diff --git a/plugins/MultiSites/Categories/MultiSitesCategory.php b/plugins/MultiSites/Categories/MultiSitesCategory.php new file mode 100644 index 0000000000..0e884d3cfb --- /dev/null +++ b/plugins/MultiSites/Categories/MultiSitesCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\MultiSites\Categories; + +use Piwik\Category\Category; + +class MultiSitesCategory extends Category +{ + protected $id = 'General_MultiSitesSummary'; + protected $order = 3; +} diff --git a/plugins/MultiSites/Reports/Base.php b/plugins/MultiSites/Reports/Base.php index 7b72df674b..fbc85200b3 100644 --- a/plugins/MultiSites/Reports/Base.php +++ b/plugins/MultiSites/Reports/Base.php @@ -15,7 +15,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_MultiSitesSummary'; + $this->categoryId = 'General_MultiSitesSummary'; $allMetricsInfo = API::getApiMetrics($enhanced = true); diff --git a/plugins/Provider/Reports/GetProvider.php b/plugins/Provider/Reports/GetProvider.php index e7cfcfa753..adf03cd26f 100644 --- a/plugins/Provider/Reports/GetProvider.php +++ b/plugins/Provider/Reports/GetProvider.php @@ -13,17 +13,26 @@ use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Provider\Columns\Provider; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetProvider extends Report { protected function init() { - $this->category = 'General_Visitors'; + $this->categoryId = 'General_Visitors'; $this->dimension = new Provider(); $this->name = Piwik::translate('Provider_ColumnProvider'); $this->documentation = Piwik::translate('Provider_ProviderReportDocumentation', '<br />'); $this->order = 50; - $this->widgetTitle = 'Provider_WidgetProviders'; + + $this->subcategoryId = 'UserCountry_SubmenuLocations'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widget = $factory->createWidget()->setName('Provider_WidgetProviders'); + $widgetsList->addWidgetConfig($widget); } public function configureView(ViewDataTable $view) diff --git a/plugins/Referrers/Categories/AllReferrersSubcategory.php b/plugins/Referrers/Categories/AllReferrersSubcategory.php new file mode 100644 index 0000000000..277fc762dd --- /dev/null +++ b/plugins/Referrers/Categories/AllReferrersSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Categories; + +use Piwik\Category\Subcategory; + +class AllReferrersSubcategory extends Subcategory +{ + protected $categoryId = 'Referrers_Referrers'; + protected $id = 'Referrers_WidgetGetAll'; + protected $order = 5; + +} diff --git a/plugins/Referrers/Categories/CampaignsSubcategory.php b/plugins/Referrers/Categories/CampaignsSubcategory.php new file mode 100644 index 0000000000..5878a0a9e2 --- /dev/null +++ b/plugins/Referrers/Categories/CampaignsSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Categories; + +use Piwik\Category\Subcategory; + +class CampaignsSubcategory extends Subcategory +{ + protected $categoryId = 'Referrers_Referrers'; + protected $id = 'Referrers_Campaigns'; + protected $order = 20; + +} diff --git a/plugins/Referrers/Categories/ReferrersCategory.php b/plugins/Referrers/Categories/ReferrersCategory.php new file mode 100644 index 0000000000..34c8f053ba --- /dev/null +++ b/plugins/Referrers/Categories/ReferrersCategory.php @@ -0,0 +1,17 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Categories; + +use Piwik\Category\Category; + +class ReferrersCategory extends Category +{ + protected $id = 'Referrers_Referrers'; + protected $order = 15; +} diff --git a/plugins/Referrers/Categories/ReferrersOverviewSubcategory.php b/plugins/Referrers/Categories/ReferrersOverviewSubcategory.php new file mode 100644 index 0000000000..9cd6dafbdc --- /dev/null +++ b/plugins/Referrers/Categories/ReferrersOverviewSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Categories; + +use Piwik\Category\Subcategory; + +class ReferrersOverviewSubcategory extends Subcategory +{ + protected $categoryId = 'Referrers_Referrers'; + protected $id = 'General_Overview'; + protected $order = 2; + +} diff --git a/plugins/Referrers/Categories/SearchEnginesSubcategory.php b/plugins/Referrers/Categories/SearchEnginesSubcategory.php new file mode 100644 index 0000000000..c8bfc478e6 --- /dev/null +++ b/plugins/Referrers/Categories/SearchEnginesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Categories; + +use Piwik\Category\Subcategory; + +class SearchEnginesSubcategory extends Subcategory +{ + protected $categoryId = 'Referrers_Referrers'; + protected $id = 'Referrers_SubmenuSearchEngines'; + protected $order = 10; + +} diff --git a/plugins/Referrers/Categories/WebsitesSubcategory.php b/plugins/Referrers/Categories/WebsitesSubcategory.php new file mode 100644 index 0000000000..8b045bd5d4 --- /dev/null +++ b/plugins/Referrers/Categories/WebsitesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Categories; + +use Piwik\Category\Subcategory; + +class WebsitesSubcategory extends Subcategory +{ + protected $categoryId = 'Referrers_Referrers'; + protected $id = 'Referrers_SubmenuWebsites'; + protected $order = 15; + +} diff --git a/plugins/Referrers/Controller.php b/plugins/Referrers/Controller.php index c628af7a83..2e1deea6af 100644 --- a/plugins/Referrers/Controller.php +++ b/plugins/Referrers/Controller.php @@ -10,10 +10,13 @@ namespace Piwik\Plugins\Referrers; use Piwik\API\Request; use Piwik\Common; +use Piwik\DataTable\Filter\CalculateEvolutionFilter; use Piwik\DataTable\Map; use Piwik\Metrics; use Piwik\Period\Range; use Piwik\Piwik; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\ViewDataTable; use Piwik\SettingsPiwik; use Piwik\Translation\Translator; use Piwik\View; @@ -35,34 +38,21 @@ class Controller extends \Piwik\Plugin\Controller parent::__construct(); } - public function index() + public function getSparklines() { - $view = new View('@Referrers/index'); - - $view->graphEvolutionReferrers = $this->getEvolutionGraph(Common::REFERRER_TYPE_DIRECT_ENTRY, array(), array('nb_visits')); - $view->nameGraphEvolutionReferrers = 'Referrers.getEvolutionGraph'; - - $nameValues = $this->getReferrersVisitorsByType(); + $metrics = $this->getReferrersVisitorsByType(); + $distinctMetrics = $this->getDistinctReferrersMetrics(); - $totalVisits = array_sum($nameValues); - foreach ($nameValues as $name => $value) { - $view->$name = $value; + $totalVisits = array_sum($metrics); + foreach ($metrics as $name => $value) { // calculate percent of total, if there were any visits - if ($value != 0 - && $totalVisits != 0 - ) { + if ($value != 0 && $totalVisits != 0) { $percentName = $name . 'Percent'; - $view->$percentName = round(($value / $totalVisits) * 100, 0); + $metrics[$percentName] = round(($value / $totalVisits) * 100, 0); } } - // set distinct metrics - $distinctMetrics = $this->getDistinctReferrersMetrics(); - foreach ($distinctMetrics as $name => $value) { - $view->$name = $value; - } - // calculate evolution for visit metrics & distinct metrics list($lastPeriodDate, $ignore) = Range::getLastDate(); if ($lastPeriodDate !== false) { @@ -74,71 +64,109 @@ class Controller extends \Piwik\Plugin\Controller // visit metrics $previousValues = $this->getReferrersVisitorsByType($lastPeriodDate); - $this->addEvolutionPropertiesToView($view, $prettyDate, $nameValues, $prettyLastPeriodDate, $previousValues); + $metrics = $this->addEvolutionPropertiesToView($prettyDate, $metrics, $prettyLastPeriodDate, $previousValues); // distinct metrics $previousValues = $this->getDistinctReferrersMetrics($lastPeriodDate); - $this->addEvolutionPropertiesToView($view, $prettyDate, $distinctMetrics, $prettyLastPeriodDate, $previousValues); + $distinctMetrics = $this->addEvolutionPropertiesToView($prettyDate, $distinctMetrics, $prettyLastPeriodDate, $previousValues); } - // sparkline for the historical data of the above values - $view->urlSparklineSearchEngines = $this->getReferrerUrlSparkline(Common::REFERRER_TYPE_SEARCH_ENGINE); - $view->urlSparklineDirectEntry = $this->getReferrerUrlSparkline(Common::REFERRER_TYPE_DIRECT_ENTRY); - $view->urlSparklineWebsites = $this->getReferrerUrlSparkline(Common::REFERRER_TYPE_WEBSITE); - $view->urlSparklineCampaigns = $this->getReferrerUrlSparkline(Common::REFERRER_TYPE_CAMPAIGN); - // sparklines for the evolution of the distinct keywords count/websites count/ etc - $view->urlSparklineDistinctSearchEngines = $this->getUrlSparkline('getLastDistinctSearchEnginesGraph'); - $view->urlSparklineDistinctKeywords = $this->getUrlSparkline('getLastDistinctKeywordsGraph'); - $view->urlSparklineDistinctWebsites = $this->getUrlSparkline('getLastDistinctWebsitesGraph'); - $view->urlSparklineDistinctCampaigns = $this->getUrlSparkline('getLastDistinctCampaignsGraph'); + /** @var Sparklines $view */ + $view = ViewDataTable\Factory::build(Sparklines::ID, $api = false, $controller = false, $force = true, $loadUserParams = false); - return $view->render(); - } + // DIRECT ENTRY + $values = array($metrics['visitorsFromDirectEntry']); + $descriptions = array(Piwik::translate('Referrers_TypeDirectEntries')); - public function allReferrers() - { - $view = new View('@Referrers/allReferrers'); + if (!empty($metrics['visitorsFromDirectEntryPercent'])) { + $values[] = $metrics['visitorsFromDirectEntryPercent']; + $descriptions[] = Piwik::translate('Referrers_XPercentOfVisits'); + } - // building the referrers summary report - $view->dataTableReferrerType = $this->renderReport('getReferrerType'); + $directEntryParams = $this->getReferrerSparklineParams(Common::REFERRER_TYPE_DIRECT_ENTRY); - $nameValues = $this->getReferrersVisitorsByType(); + $view->config->addSparkline($directEntryParams, $values, $descriptions, @$metrics['visitorsFromDirectEntryEvolution']); - $totalVisits = array_sum($nameValues); - foreach ($nameValues as $name => $value) { - $view->$name = $value; - // calculate percent of total, if there were any visits - if ($value != 0 - && $totalVisits != 0 - ) { - $percentName = $name . 'Percent'; - $view->$percentName = round(($value / $totalVisits) * 100, 0); - } + // WEBSITES + $values = array($metrics['visitorsFromWebsites']); + $descriptions = array(Piwik::translate('Referrers_TypeWebsites')); + + if (!empty($metrics['visitorsFromWebsitesPercent'])) { + $values[] = $metrics['visitorsFromWebsitesPercent']; + $descriptions[] = Piwik::translate('Referrers_XPercentOfVisits'); } - $view->totalVisits = $totalVisits; - $view->referrersReportsByDimension = $this->renderReport('getAll'); + $searchEngineParams = $this->getReferrerSparklineParams(Common::REFERRER_TYPE_WEBSITE); - return $view->render(); - } + $view->config->addSparkline($searchEngineParams, $values, $descriptions, @$metrics['visitorsFromWebsitesEvolution']); + + + // SEARCH ENGINES + $values = array($metrics['visitorsFromSearchEngines']); + $descriptions = array(Piwik::translate('Referrers_TypeSearchEngines')); + + if (!empty($metrics['visitorsFromSearchEnginesPercent'])) { + $values[] = $metrics['visitorsFromSearchEnginesPercent']; + $descriptions[] = Piwik::translate('Referrers_XPercentOfVisits'); + } + $searchEngineParams = $this->getReferrerSparklineParams(Common::REFERRER_TYPE_SEARCH_ENGINE); + + $view->config->addSparkline($searchEngineParams, $values, $descriptions, @$metrics['visitorsFromSearchEnginesEvolution']); + + + // CAMPAIGNS + $values = array($metrics['visitorsFromCampaigns']); + $descriptions = array(Piwik::translate('Referrers_TypeCampaigns')); + + if (!empty($metrics['visitorsFromCampaignsPercent'])) { + $values[] = $metrics['visitorsFromCampaignsPercent']; + $descriptions[] = Piwik::translate('Referrers_XPercentOfVisits'); + } + + $searchEngineParams = $this->getReferrerSparklineParams(Common::REFERRER_TYPE_CAMPAIGN); + + $view->config->addSparkline($searchEngineParams, $values, $descriptions, @$metrics['visitorsFromCampaignsEvolution']); + + + // DISTINCT SEARCH ENGINES + $sparklineParams = $this->getDistinctSparklineUrlParams('getLastDistinctSearchEnginesGraph'); + $value = $distinctMetrics['numberDistinctSearchEngines']; + $description = Piwik::translate('Referrers_DistinctSearchEngines'); + + $view->config->addSparkline($sparklineParams, $value, $description, @$distinctMetrics['numberDistinctSearchEnginesEvolution']); + + + // DISTINCT WEBSITES + $sparklineParams = $this->getDistinctSparklineUrlParams('getLastDistinctWebsitesGraph'); + $values = array($distinctMetrics['numberDistinctWebsites'], $distinctMetrics['numberDistinctWebsitesUrls']); + $descriptions = array(Piwik::translate('Referrers_DistinctWebsites'), Piwik::translate('Referrers_UsingNDistinctUrls')); + + $view->config->addSparkline($sparklineParams, $values, $descriptions, @$distinctMetrics['numberDistinctWebsitesEvolution']); + + + // DISTINCT KEYWORDS + $sparklineParams = $this->getDistinctSparklineUrlParams('getLastDistinctKeywordsGraph'); + $value = $distinctMetrics['numberDistinctKeywords']; + $description = Piwik::translate('Referrers_DistinctKeywords'); + + $view->config->addSparkline($sparklineParams, $value, $description, @$distinctMetrics['numberDistinctKeywordsEvolution']); + + + // DISTINCT CAMPAIGNS + $sparklineParams = $this->getDistinctSparklineUrlParams('getLastDistinctCampaignsGraph'); + $value = $distinctMetrics['numberDistinctCampaigns']; + $description = Piwik::translate('Referrers_DistinctCampaigns'); + + $view->config->addSparkline($sparklineParams, $value, $description, @$distinctMetrics['numberDistinctCampaignsEvolution']); - public function getSearchEnginesAndKeywords() - { - $view = new View('@Referrers/getSearchEnginesAndKeywords'); - $view->searchEngines = $this->renderReport('getSearchEngines'); - $view->keywords = $this->renderReport('getKeywords'); return $view->render(); } - public function indexWebsites() + private function getDistinctSparklineUrlParams($action) { - $view = new View('@Referrers/indexWebsites'); - $view->websites = $this->renderReport('getWebsites'); - $view->socials = $this->renderReport('getSocials'); - - return $view->render(); + return array('module' => $this->pluginName, 'action' => $action); } protected function getReferrersVisitorsByType($date = false) @@ -221,7 +249,7 @@ class Controller extends \Piwik\Plugin\Controller } else { // use $typeReferrer as default if ($typeReferrer === false) { - $typeReferrer = Common::getRequestVar('typeReferrer', false); + $typeReferrer = Common::getRequestVar('typeReferrer', Common::REFERRER_TYPE_DIRECT_ENTRY); } $label = self::getTranslatedReferrerTypeLabel($typeReferrer); $total = $this->translator->translate('General_Total'); @@ -416,14 +444,16 @@ function DisplayTopKeywords($url = "") * @param int $referrerType The referrer type. Referrer types are defined in Common class. * @return string The URL that can be used to get a sparkline image. */ - private function getReferrerUrlSparkline($referrerType) + private function getReferrerSparklineParams($referrerType) { $totalRow = $this->translator->translate('General_Total'); - return $this->getUrlSparkline( - 'getEvolutionGraph', - array('columns' => array('nb_visits'), - 'rows' => array(self::getTranslatedReferrerTypeLabel($referrerType), $totalRow), - 'typeReferrer' => $referrerType) + + return array( + 'columns' => array('nb_visits'), + 'rows' => array(self::getTranslatedReferrerTypeLabel($referrerType), $totalRow), + 'typeReferrer' => $referrerType, + 'module' => $this->pluginName, + 'action' => 'getReferrerType' ); } @@ -456,20 +486,32 @@ function DisplayTopKeywords($url = "") * Utility method that calculates evolution values for a set of current & past values * and sets properties on a View w/ HTML that displays the evolution percents. * - * @param View $view The view to set properties on. * @param string $date The date of the current values. * @param array $currentValues Array mapping view property names w/ present values. * @param string $lastPeriodDate The date of the period in the past. * @param array $previousValues Array mapping view property names w/ past values. Keys * in this array should be the same as keys in $currentValues. + * @return array Added current values */ - private function addEvolutionPropertiesToView($view, $date, $currentValues, $lastPeriodDate, $previousValues) + private function addEvolutionPropertiesToView($date, $currentValues, $lastPeriodDate, $previousValues) { foreach ($previousValues as $name => $pastValue) { $currentValue = $currentValues[$name]; $evolutionName = $name . 'Evolution'; - $view->$evolutionName = $this->getEvolutionHtml($date, $currentValue, $lastPeriodDate, $pastValue); + $currentValues[$evolutionName] = array( + 'currentValue' => $currentValue, + 'pastValue' => $pastValue, + 'tooltip' => Piwik::translate('General_EvolutionSummaryGeneric', array( + Piwik::translate('General_NVisits', $currentValue), + $date, + Piwik::translate('General_NVisits', $pastValue), + $lastPeriodDate, + CalculateEvolutionFilter::calculate($currentValue, $pastValue, $precision = 1) + )) + ); } + + return $currentValues; } } diff --git a/plugins/Referrers/Menu.php b/plugins/Referrers/Menu.php deleted file mode 100644 index cb92a43db3..0000000000 --- a/plugins/Referrers/Menu.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Referrers; - -use Piwik\Menu\MenuReporting; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addReferrersItem('', $this->urlForAction('index'), 20); - $menu->addReferrersItem('General_Overview', $this->urlForAction('index'), 1); - $menu->addReferrersItem('Referrers_WidgetGetAll', $this->urlForAction('allReferrers'), 2); - $menu->addReferrersItem('Referrers_SubmenuSearchEngines', $this->urlForAction('getSearchEnginesAndKeywords'), 3); - $menu->addReferrersItem('Referrers_SubmenuWebsites', $this->urlForAction('indexWebsites'), 4); - } -} diff --git a/plugins/Referrers/Reports/Base.php b/plugins/Referrers/Reports/Base.php index e4a6394c00..2bd240d191 100644 --- a/plugins/Referrers/Reports/Base.php +++ b/plugins/Referrers/Reports/Base.php @@ -12,7 +12,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'Referrers_Referrers'; + $this->categoryId = 'Referrers_Referrers'; } } diff --git a/plugins/Referrers/Reports/GetAll.php b/plugins/Referrers/Reports/GetAll.php index 2079dd414d..cb5f17ee41 100644 --- a/plugins/Referrers/Reports/GetAll.php +++ b/plugins/Referrers/Reports/GetAll.php @@ -13,6 +13,8 @@ use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\Plugins\Referrers\Columns\Referrer; use Piwik\Plugins\Referrers\Referrers; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetAll extends Base { @@ -23,7 +25,15 @@ class GetAll extends Base $this->name = Piwik::translate('Referrers_WidgetGetAll'); $this->documentation = Piwik::translate('Referrers_AllReferrersReportDocumentation', '<br />'); $this->order = 2; - $this->widgetTitle = 'Referrers_WidgetGetAll'; + + $this->subcategoryId = 'Referrers_WidgetGetAll'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget()->setName('Referrers_Referrers') + ); } public function getDefaultTypeViewDataTable() diff --git a/plugins/Referrers/Reports/GetCampaigns.php b/plugins/Referrers/Reports/GetCampaigns.php index a09affa70c..1d375423d8 100644 --- a/plugins/Referrers/Reports/GetCampaigns.php +++ b/plugins/Referrers/Reports/GetCampaigns.php @@ -24,8 +24,8 @@ class GetCampaigns extends Base $this->actionToLoadSubTables = 'getKeywordsFromCampaignId'; $this->hasGoalMetrics = true; $this->order = 9; - $this->widgetTitle = 'Referrers_Campaigns'; - $this->menuTitle = 'Referrers_Campaigns'; + + $this->subcategoryId = 'Referrers_Campaigns'; } public function configureView(ViewDataTable $view) diff --git a/plugins/Referrers/Reports/GetKeywords.php b/plugins/Referrers/Reports/GetKeywords.php index be8e8d3f4c..3239fefde0 100644 --- a/plugins/Referrers/Reports/GetKeywords.php +++ b/plugins/Referrers/Reports/GetKeywords.php @@ -11,7 +11,9 @@ namespace Piwik\Plugins\Referrers\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; use Piwik\Plugins\Referrers\Columns\Keyword; +use Piwik\Tracker\Visit; class GetKeywords extends Base { @@ -24,7 +26,7 @@ class GetKeywords extends Base $this->actionToLoadSubTables = 'getSearchEnginesFromKeywordId'; $this->hasGoalMetrics = true; $this->order = 3; - $this->widgetTitle = 'Referrers_Keywords'; + $this->subcategoryId = 'Referrers_SubmenuSearchEngines'; } public function configureView(ViewDataTable $view) diff --git a/plugins/Referrers/Reports/GetReferrerType.php b/plugins/Referrers/Reports/GetReferrerType.php index f5656bfa7f..c09f8c6d17 100644 --- a/plugins/Referrers/Reports/GetReferrerType.php +++ b/plugins/Referrers/Reports/GetReferrerType.php @@ -12,7 +12,11 @@ use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; use Piwik\Plugins\Referrers\Columns\ReferrerType; +use Piwik\Widget\WidgetsList; +use Piwik\Report\ReportWidgetFactory; class GetReferrerType extends Base { @@ -32,7 +36,7 @@ class GetReferrerType extends Base $this->constantRowsCount = true; $this->hasGoalMetrics = true; $this->order = 1; - $this->widgetTitle = 'General_Overview'; + $this->subcategoryId = 'Referrers_WidgetGetAll'; } public function getDefaultTypeViewDataTable() @@ -40,6 +44,36 @@ class GetReferrerType extends Base return HtmlTable\AllColumns::ID; } + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('Referrers_ReferrerTypes') + ->setSubcategoryId('Referrers_WidgetGetAll') + ); + + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('General_EvolutionOverPeriod') + ->setSubcategoryId('General_Overview') + ->setAction('getEvolutionGraph') + ->setIsNotWidgetizable() + ->forceViewDataTable(Evolution::ID) + ->addParameters(array( + 'columns' => $defaultColumns = array('nb_visits'), + )) + ); + + $widgetsList->addWidgetConfig( + $factory->createCustomWidget('getSparklines') + ->forceViewDataTable(Sparklines::ID) + ->setIsNotWidgetizable() + ->setName('Referrers_Type') + ->setSubcategoryId('General_Overview') + ->setOrder(10) + ); + } + public function configureView(ViewDataTable $view) { $idSubtable = Common::getRequestVar('idSubtable', false); diff --git a/plugins/Referrers/Reports/GetSearchEngines.php b/plugins/Referrers/Reports/GetSearchEngines.php index 3b66cd0784..0a17cd132e 100644 --- a/plugins/Referrers/Reports/GetSearchEngines.php +++ b/plugins/Referrers/Reports/GetSearchEngines.php @@ -24,7 +24,8 @@ class GetSearchEngines extends Base $this->actionToLoadSubTables = 'getKeywordsFromSearchEngineId'; $this->hasGoalMetrics = true; $this->order = 7; - $this->widgetTitle = 'Referrers_SearchEngines'; + + $this->subcategoryId = 'Referrers_SubmenuSearchEngines'; } public function configureView(ViewDataTable $view) diff --git a/plugins/Referrers/Reports/GetSocials.php b/plugins/Referrers/Reports/GetSocials.php index b49fac623a..7429ddd48f 100644 --- a/plugins/Referrers/Reports/GetSocials.php +++ b/plugins/Referrers/Reports/GetSocials.php @@ -14,6 +14,8 @@ use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Pie; use Piwik\Plugins\Referrers\Columns\SocialNetwork; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetSocials extends Base { @@ -25,7 +27,14 @@ class GetSocials extends Base $this->documentation = Piwik::translate('Referrers_WebsitesReportDocumentation', '<br />'); $this->actionToLoadSubTables = 'getUrlsForSocial'; $this->order = 11; - $this->widgetTitle = 'Referrers_WidgetSocials'; + + $this->subcategoryId = 'Referrers_SubmenuWebsites'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widget = $factory->createWidget()->setName('Referrers_Socials'); + $widgetsList->addWidgetConfig($widget); } public function getDefaultTypeViewDataTable() diff --git a/plugins/Referrers/Reports/GetWebsites.php b/plugins/Referrers/Reports/GetWebsites.php index 18f9336a63..f5be3ca375 100644 --- a/plugins/Referrers/Reports/GetWebsites.php +++ b/plugins/Referrers/Reports/GetWebsites.php @@ -12,6 +12,8 @@ use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\Plugins\Referrers\Columns\Website; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetWebsites extends Base { @@ -25,7 +27,8 @@ class GetWebsites extends Base $this->actionToLoadSubTables = 'getUrlsFromWebsiteId'; $this->hasGoalMetrics = true; $this->order = 5; - $this->widgetTitle = 'Referrers_WidgetExternalWebsites'; + + $this->subcategoryId = 'Referrers_SubmenuWebsites'; } public function configureView(ViewDataTable $view) diff --git a/plugins/Referrers/Widgets.php b/plugins/Referrers/Widgets.php deleted file mode 100644 index 4e23c9f746..0000000000 --- a/plugins/Referrers/Widgets.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\Referrers; - -use Piwik\SettingsPiwik; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'SEO'; - - public function init() - { - if (SettingsPiwik::isSegmentationEnabled()) { - $this->addWidget('Referrers_WidgetTopKeywordsForPages', 'getKeywordsForPage'); - } - } - -} diff --git a/plugins/Referrers/Widgets/GetKeywordsForPage.php b/plugins/Referrers/Widgets/GetKeywordsForPage.php new file mode 100644 index 0000000000..165bb18f68 --- /dev/null +++ b/plugins/Referrers/Widgets/GetKeywordsForPage.php @@ -0,0 +1,23 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\Referrers\Widgets; + +use Piwik\Widget\WidgetConfig; +use Piwik\SettingsPiwik; + +class GetKeywordsForPage extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('SEO'); + $config->setName('Referrers_WidgetTopKeywordsForPages'); + $config->setIsEnabled(SettingsPiwik::isSegmentationEnabled()); + } + +} diff --git a/plugins/Referrers/templates/allReferrers.twig b/plugins/Referrers/templates/allReferrers.twig deleted file mode 100644 index bf8c7c431c..0000000000 --- a/plugins/Referrers/templates/allReferrers.twig +++ /dev/null @@ -1,11 +0,0 @@ -<h2 piwik-enriched-headline>{{ 'Referrers_ReferrerTypes'|translate }}</h2> -{{ dataTableReferrerType|raw }} - -<div style="clear:both;"></div> - -{% if totalVisits > 0 %} - <h2 piwik-enriched-headline>{{ 'Referrers_Referrers'|translate }}</h2> - {{ referrersReportsByDimension|raw }} -{% endif %} - -{% include "_sparklineFooter.twig" %} diff --git a/plugins/Referrers/templates/getSearchEnginesAndKeywords.twig b/plugins/Referrers/templates/getSearchEnginesAndKeywords.twig deleted file mode 100644 index 29b2b3f0af..0000000000 --- a/plugins/Referrers/templates/getSearchEnginesAndKeywords.twig +++ /dev/null @@ -1,13 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'Referrers_Keywords'|translate }}</h2> - {{ keywords|raw }} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'Referrers_SearchEngines'|translate }}</h2> - {{ searchEngines|raw }} - </div> - -</div> diff --git a/plugins/Referrers/templates/index.twig b/plugins/Referrers/templates/index.twig deleted file mode 100644 index c629431210..0000000000 --- a/plugins/Referrers/templates/index.twig +++ /dev/null @@ -1,89 +0,0 @@ -<h2 piwik-enriched-headline - data-graph-id="{{ nameGraphEvolutionReferrers }}">{{ 'General_EvolutionOverPeriod'|translate }}</h2> -{{ graphEvolutionReferrers|raw }} - -<h2 piwik-enriched-headline>{{ 'Referrers_Type'|translate }}</h2> - -<div class="row"> - <div class="col-md-6"> - <div class="sparkline" style="padding-left: 12px;">{{ sparkline(urlSparklineDirectEntry) }} - {{ 'Referrers_TypeDirectEntries'|translate("<strong>"~visitorsFromDirectEntry~"</strong>")|raw }} - {% if visitorsFromDirectEntryPercent|default is not empty %}, - {{ 'Referrers_XPercentOfVisits'|translate("<strong>"~visitorsFromDirectEntryPercent~"</strong>")|raw }} - {% endif %} - {% if visitorsFromDirectEntryEvolution|default is not empty %} - {{ visitorsFromDirectEntryEvolution|raw }} - {% endif %} - </div> - <div class="sparkline" style="padding-left: 12px;">{{ sparkline(urlSparklineSearchEngines) }} - {{ 'Referrers_TypeSearchEngines'|translate("<strong>"~visitorsFromSearchEngines~"</strong>")|raw }} - {% if visitorsFromSearchEnginesPercent|default is not empty %}, - {{ 'Referrers_XPercentOfVisits'|translate("<strong>"~visitorsFromSearchEnginesPercent~"</strong>")|raw }} - {% endif %} - {% if visitorsFromSearchEnginesEvolution|default is not empty %} - {{ visitorsFromSearchEnginesEvolution|raw }} - {% endif %} - </div> - </div> - <div class="col-md-6"> - <div class="sparkline">{{ sparkline(urlSparklineWebsites) }} - {{ 'Referrers_TypeWebsites'|translate("<strong>"~visitorsFromWebsites~"</strong>")|raw }} - {% if visitorsFromWebsitesPercent|default is not empty %}, - {{ 'Referrers_XPercentOfVisits'|translate("<strong>"~visitorsFromWebsitesPercent~"</strong>")|raw }} - {% endif %} - {% if visitorsFromWebsitesEvolution|default is not empty %} - {{ visitorsFromWebsitesEvolution|raw }} - {% endif %} - </div> - <div class="sparkline">{{ sparkline(urlSparklineCampaigns) }} - {{ 'Referrers_TypeCampaigns'|translate("<strong>"~visitorsFromCampaigns~"</strong>")|raw }} - {% if visitorsFromCampaignsPercent|default is not empty %}, - {{ 'Referrers_XPercentOfVisits'|translate("<strong>"~visitorsFromCampaignsPercent~"</strong>")|raw }} - {% endif %} - {% if visitorsFromCampaignsEvolution|default is not empty %} - {{ visitorsFromCampaignsEvolution|raw }} - {% endif %} - </div> - </div> -</div> - -<div id="distinctReferrersByType" class="row"> - <div class="col-md-6"> - <div class="sparkline" style="padding-left: 12px;">{{ sparkline(urlSparklineDistinctSearchEngines) }} - <strong>{{ numberDistinctSearchEngines }}</strong> {{ 'Referrers_DistinctSearchEngines'|translate }} - {% if numberDistinctSearchEnginesEvolution|default is not empty %} - {{ numberDistinctSearchEnginesEvolution|raw }} - {% endif %} - </div> - <div class="sparkline" style="padding-left: 12px;">{{ sparkline(urlSparklineDistinctKeywords) }} - <strong>{{ numberDistinctKeywords }}</strong> {{ 'Referrers_DistinctKeywords'|translate }} - {% if numberDistinctKeywordsEvolution|default is not empty %} - {{ numberDistinctKeywordsEvolution|raw }} - {% endif %} - </div> - </div> - <div class="col-md-6"> - <div class="sparkline">{{ sparkline(urlSparklineDistinctWebsites) }} - <strong>{{ numberDistinctWebsites }}</strong> {{ 'Referrers_DistinctWebsites'|translate }} - {{ 'Referrers_UsingNDistinctUrls'|translate("<strong>"~numberDistinctWebsitesUrls~"</strong>")|raw }} - {% if numberDistinctWebsitesEvolution|default is not empty %} - {{ numberDistinctWebsitesEvolution|raw }} - {% endif %} - </div> - <div class="sparkline">{{ sparkline(urlSparklineDistinctCampaigns) }} - <strong>{{ numberDistinctCampaigns }}</strong> {{ 'Referrers_DistinctCampaigns'|translate }} - {% if numberDistinctCampaignsEvolution|default is not empty %} - {{ numberDistinctCampaignsEvolution|raw }} - {% endif %} - </div> - </div> - <br/> -</div> - -<div style="float:left;" class="relatedReferrerReports">{{ 'General_View'|translate }} - <a href="javascript:broadcast.propagateAjax('module=Referrers&action=getSearchEnginesAndKeywords')">{{ 'Referrers_SubmenuSearchEngines'|translate }}</a>, - <a href="javascript:broadcast.propagateAjax('module=Referrers&action=indexWebsites')">{{ 'Referrers_SubmenuWebsites'|translate }}</a>, - <a href="javascript:broadcast.propagateAjax('module=Referrers&action=indexCampaigns')">{{ 'Referrers_Campaigns'|translate }}</a>. -</div> - -{% include "_sparklineFooter.twig" %} diff --git a/plugins/Referrers/templates/indexWebsites.twig b/plugins/Referrers/templates/indexWebsites.twig deleted file mode 100644 index cdfa6efe01..0000000000 --- a/plugins/Referrers/templates/indexWebsites.twig +++ /dev/null @@ -1,13 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'Referrers_Websites'|translate }}</h2> - {{ websites|raw }} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'Referrers_Socials'|translate }}</h2> - {{ socials|raw }} - </div> - -</div> diff --git a/plugins/Resolution/Reports/Base.php b/plugins/Resolution/Reports/Base.php index 259a164113..0ef3f0d631 100644 --- a/plugins/Resolution/Reports/Base.php +++ b/plugins/Resolution/Reports/Base.php @@ -15,7 +15,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_VisitorSettings'; + $this->categoryId = 'General_Visitors'; } protected function getBasicResolutionDisplayProperties(ViewDataTable $view) diff --git a/plugins/Resolution/Reports/GetConfiguration.php b/plugins/Resolution/Reports/GetConfiguration.php index c01c4f3b07..600f07edd9 100644 --- a/plugins/Resolution/Reports/GetConfiguration.php +++ b/plugins/Resolution/Reports/GetConfiguration.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Resolution\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Resolution\Columns\Configuration; +use Piwik\Plugin\Reports; class GetConfiguration extends Base { @@ -18,10 +19,11 @@ class GetConfiguration extends Base { parent::init(); $this->dimension = new Configuration(); - $this->name = Piwik::translate('Resolution_WidgetGlobalVisitors'); + $this->name = Piwik::translate('Resolution_Configurations'); $this->documentation = Piwik::translate('Resolution_WidgetGlobalVisitorsDocumentation', '<br />'); $this->order = 7; - $this->widgetTitle = 'Resolution_WidgetGlobalVisitors'; + + $this->subcategoryId = 'DevicesDetection_Software'; } public function configureView(ViewDataTable $view) @@ -36,7 +38,7 @@ class GetConfiguration extends Base public function getRelatedReports() { return array( - self::factory('Resolution', 'getResolution'), + Reports::factory('Resolution', 'getResolution'), ); } } diff --git a/plugins/Resolution/Reports/GetResolution.php b/plugins/Resolution/Reports/GetResolution.php index f9066ce19c..3b27236a86 100644 --- a/plugins/Resolution/Reports/GetResolution.php +++ b/plugins/Resolution/Reports/GetResolution.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Resolution\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\Resolution\Columns\Resolution; +use Piwik\Plugin\Reports; class GetResolution extends Base { @@ -20,8 +21,9 @@ class GetResolution extends Base $this->dimension = new Resolution(); $this->name = Piwik::translate('Resolution_WidgetResolutions'); $this->documentation = ''; // TODO - $this->order = 0; - $this->widgetTitle = 'Resolution_WidgetResolutions'; + $this->order = 8; + + $this->subcategoryId = 'DevicesDetection_Devices'; } public function configureView(ViewDataTable $view) @@ -34,7 +36,7 @@ class GetResolution extends Base public function getRelatedReports() { return array( - self::factory('Resolution', 'getConfiguration'), + Reports::factory('Resolution', 'getConfiguration'), ); } } diff --git a/plugins/SEO/Widgets.php b/plugins/SEO/Widgets/GetRank.php index 1f06c2ec8b..521bf1859c 100644 --- a/plugins/SEO/Widgets.php +++ b/plugins/SEO/Widgets/GetRank.php @@ -6,25 +6,26 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later * */ -namespace Piwik\Plugins\SEO; +namespace Piwik\Plugins\SEO\Widgets; use Piwik\Common; use Piwik\DataTable\Renderer; +use Piwik\Widget\WidgetConfig; use Piwik\Site; use Piwik\Url; use Piwik\UrlHelper; use Piwik\View; +use Piwik\Plugins\SEO\API; -class Widgets extends \Piwik\Plugin\Widgets +class GetRank extends \Piwik\Widget\Widget { - protected $category = 'SEO'; - - public function init() + public static function configure(WidgetConfig $config) { - $this->addWidget('SEO_SeoRankings', 'getRank'); + $config->setCategoryId('SEO'); + $config->setName('SEO_SeoRankings'); } - public function getRank() + public function render() { $idSite = Common::getRequestVar('idSite'); $site = new Site($idSite); diff --git a/plugins/Transitions/Controller.php b/plugins/Transitions/Controller.php index dbd782f08c..fb7bb5de2b 100644 --- a/plugins/Transitions/Controller.php +++ b/plugins/Transitions/Controller.php @@ -22,7 +22,7 @@ class Controller extends \Piwik\Plugin\Controller * translation keys. */ private static $metricTranslations = array( - 'pageviewsInline' => 'VisitsSummary_NbPageviewsDescription', + 'pageviewsInline' => 'Transitions_NumPageviews', 'loopsInline' => 'Transitions_LoopsInline', 'fromPreviousPages' => 'Transitions_FromPreviousPages', 'fromPreviousPagesInline' => 'Transitions_FromPreviousPagesInline', @@ -41,9 +41,9 @@ class Controller extends \Piwik\Plugin\Controller 'toFollowingSiteSearches' => 'Transitions_ToFollowingSiteSearches', 'toFollowingSiteSearchesInline' => 'Transitions_ToFollowingSiteSearchesInline', 'downloads' => 'General_Downloads', - 'downloadsInline' => 'VisitsSummary_NbDownloadsDescription', + 'downloadsInline' => 'Transitions_NumDownloads', 'outlinks' => 'General_Outlinks', - 'outlinksInline' => 'VisitsSummary_NbOutlinksDescription', + 'outlinksInline' => 'Transitions_NumOutlinks', 'exits' => 'General_ColumnExits', 'exitsInline' => 'Transitions_ExitsInline', 'bouncesInline' => 'Transitions_BouncesInline' diff --git a/plugins/Transitions/lang/en.json b/plugins/Transitions/lang/en.json index 4e76d5bc54..af45334071 100644 --- a/plugins/Transitions/lang/en.json +++ b/plugins/Transitions/lang/en.json @@ -4,6 +4,9 @@ "DirectEntries": "Direct Entries", "ErrorBack": "Go back to the previous action", "ExitsInline": "%s exits", + "NumPageviews": "%s pageviews", + "NumDownloads": "%s downloads", + "NumOutlinks": "%s outlinks", "FromCampaigns": "From Campaigns", "FromPreviousPages": "From Internal Pages", "FromPreviousPagesInline": "%s from internal pages", diff --git a/plugins/UserCountry/Categories/LocationsSubcategory.php b/plugins/UserCountry/Categories/LocationsSubcategory.php new file mode 100644 index 0000000000..22be202abd --- /dev/null +++ b/plugins/UserCountry/Categories/LocationsSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\UserCountry\Categories; + +use Piwik\Category\Subcategory; + +class LocationsSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'UserCountry_SubmenuLocations'; + protected $order = 25; + +} diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php index a241968cbd..0ed417e421 100644 --- a/plugins/UserCountry/Controller.php +++ b/plugins/UserCountry/Controller.php @@ -26,18 +26,13 @@ use Piwik\View; */ class Controller extends \Piwik\Plugin\ControllerAdmin { - public function index() + public function getDistinctCountries() { - $view = new View('@UserCountry/index'); + $view = new View('@UserCountry/getDistinctCountries'); $view->urlSparklineCountries = $this->getUrlSparkline('getLastDistinctCountriesGraph'); $view->numberDistinctCountries = $this->getNumberOfDistinctCountries(true); - $view->dataTableCountry = $this->renderReport('getCountry'); - $view->dataTableContinent = $this->renderReport('getContinent'); - $view->dataTableRegion = $this->renderReport('getRegion'); - $view->dataTableCity = $this->renderReport('getCity'); - return $view->render(); } diff --git a/plugins/UserCountry/Menu.php b/plugins/UserCountry/Menu.php index fa90e258db..92f88a7f14 100644 --- a/plugins/UserCountry/Menu.php +++ b/plugins/UserCountry/Menu.php @@ -9,7 +9,6 @@ namespace Piwik\Plugins\UserCountry; use Piwik\Menu\MenuAdmin; -use Piwik\Menu\MenuReporting; use Piwik\Piwik; class Menu extends \Piwik\Plugin\Menu @@ -22,9 +21,4 @@ class Menu extends \Piwik\Plugin\Menu $order = 9); } } - - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addVisitorsItem('UserCountry_SubmenuLocations', $this->urlForAction('index')); - } } diff --git a/plugins/UserCountry/Reports/Base.php b/plugins/UserCountry/Reports/Base.php index f7a4f34203..c24ec82d24 100644 --- a/plugins/UserCountry/Reports/Base.php +++ b/plugins/UserCountry/Reports/Base.php @@ -17,7 +17,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_Visitors'; + $this->categoryId = 'General_Visitors'; } protected function getGeoIPReportDocSuffix() diff --git a/plugins/UserCountry/Reports/GetCity.php b/plugins/UserCountry/Reports/GetCity.php index ce087c3f3c..e492d12ee2 100644 --- a/plugins/UserCountry/Reports/GetCity.php +++ b/plugins/UserCountry/Reports/GetCity.php @@ -22,9 +22,8 @@ class GetCity extends Base $this->documentation = Piwik::translate('UserCountry_getCityDocumentation') . '<br/>' . $this->getGeoIPReportDocSuffix(); $this->metrics = array('nb_visits', 'nb_uniq_visitors', 'nb_actions'); $this->hasGoalMetrics = true; - $this->order = 8; - $this->widgetTitle = Piwik::translate('UserCountry_WidgetLocation') - . ' (' . Piwik::translate('UserCountry_City') . ')'; + $this->order = 10; + $this->subcategoryId = 'UserCountry_SubmenuLocations'; } public function configureView(ViewDataTable $view) diff --git a/plugins/UserCountry/Reports/GetContinent.php b/plugins/UserCountry/Reports/GetContinent.php index 7ba6d2ca1c..b524c4f048 100644 --- a/plugins/UserCountry/Reports/GetContinent.php +++ b/plugins/UserCountry/Reports/GetContinent.php @@ -11,6 +11,9 @@ namespace Piwik\Plugins\UserCountry\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\UserCountry\Columns\Continent; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetContainerConfig; +use Piwik\Widget\WidgetsList; class GetContinent extends Base { @@ -23,8 +26,18 @@ class GetContinent extends Base $this->metrics = array('nb_visits', 'nb_uniq_visitors', 'nb_actions'); $this->hasGoalMetrics = true; $this->order = 6; - $this->widgetTitle = Piwik::translate('UserCountry_WidgetLocation') - . ' (' . Piwik::translate('UserCountry_Continent') . ')'; + + $this->subcategoryId = 'UserCountry_SubmenuLocations'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig($factory->createContainerWidget('Continent')); + + $widgetsList->addToContainerWidget('Continent', $factory->createWidget()); + + $widget = $factory->createWidget()->setAction('getDistinctCountries')->setName(''); + $widgetsList->addToContainerWidget('Continent', $widget); } public function configureView(ViewDataTable $view) diff --git a/plugins/UserCountry/Reports/GetCountry.php b/plugins/UserCountry/Reports/GetCountry.php index 88363e1efa..5b038f7e49 100644 --- a/plugins/UserCountry/Reports/GetCountry.php +++ b/plugins/UserCountry/Reports/GetCountry.php @@ -24,8 +24,7 @@ class GetCountry extends Base $this->metrics = array('nb_visits', 'nb_uniq_visitors', 'nb_actions'); $this->hasGoalMetrics = true; $this->order = 5; - $this->widgetTitle = Piwik::translate('UserCountry_WidgetLocation') - . ' (' . Piwik::translate('UserCountry_Country') . ')'; + $this->subcategoryId = 'UserCountry_SubmenuLocations'; } public function configureView(ViewDataTable $view) diff --git a/plugins/UserCountry/Reports/GetRegion.php b/plugins/UserCountry/Reports/GetRegion.php index 648b78967b..7e418ebef2 100644 --- a/plugins/UserCountry/Reports/GetRegion.php +++ b/plugins/UserCountry/Reports/GetRegion.php @@ -23,8 +23,8 @@ class GetRegion extends Base $this->metrics = array('nb_visits', 'nb_uniq_visitors', 'nb_actions'); $this->hasGoalMetrics = true; $this->order = 7; - $this->widgetTitle = Piwik::translate('UserCountry_WidgetLocation') - . ' (' . Piwik::translate('UserCountry_Region') . ')'; + + $this->subcategoryId = 'UserCountry_SubmenuLocations'; } public function configureView(ViewDataTable $view) diff --git a/plugins/UserCountry/templates/getDistinctCountries.twig b/plugins/UserCountry/templates/getDistinctCountries.twig new file mode 100644 index 0000000000..7a2506f782 --- /dev/null +++ b/plugins/UserCountry/templates/getDistinctCountries.twig @@ -0,0 +1,5 @@ +<div class="sparkline"> + {{ sparkline(urlSparklineCountries) }} + {{ 'UserCountry_DistinctCountries'|translate("<strong>"~numberDistinctCountries~"</strong>")|raw }} +</div> +<br style="clear:left"/>
\ No newline at end of file diff --git a/plugins/UserCountry/templates/index.twig b/plugins/UserCountry/templates/index.twig deleted file mode 100644 index fe1a6735dd..0000000000 --- a/plugins/UserCountry/templates/index.twig +++ /dev/null @@ -1,29 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - {{ postEvent("Template.leftColumnUserCountry") }} - - <h2 piwik-enriched-headline>{{ 'UserCountry_Continent'|translate }}</h2> - {{ dataTableContinent|raw }} - - <div class="sparkline"> - {{ sparkline(urlSparklineCountries) }} - {{ 'UserCountry_DistinctCountries'|translate("<strong>"~numberDistinctCountries~"</strong>")|raw }} - </div> - <div style="clear:left"></div> - - {{ postEvent("Template.footerUserCountry") }} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'UserCountry_Country'|translate }}</h2> - {{ dataTableCountry|raw }} - - <h2 piwik-enriched-headline>{{ 'UserCountry_Region'|translate }}</h2> - {{ dataTableRegion|raw }} - - <h2 piwik-enriched-headline>{{ 'UserCountry_City'|translate }}</h2> - {{ dataTableCity|raw }} - </div> - -</div> diff --git a/plugins/UserCountryMap/Categories/RealTimeMapSubcategory.php b/plugins/UserCountryMap/Categories/RealTimeMapSubcategory.php new file mode 100644 index 0000000000..d8f482327f --- /dev/null +++ b/plugins/UserCountryMap/Categories/RealTimeMapSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\UserCountryMap\Categories; + +use Piwik\Category\Subcategory; + +class RealTimeMapSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'UserCountryMap_RealTimeMap'; + protected $order = 40; + +} diff --git a/plugins/UserCountryMap/Menu.php b/plugins/UserCountryMap/Menu.php deleted file mode 100644 index 88108e3f4d..0000000000 --- a/plugins/UserCountryMap/Menu.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\UserCountryMap; - -use Piwik\Menu\MenuReporting; -use Piwik\Plugin\Manager as PluginManager; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - if (PluginManager::getInstance()->isPluginActivated('UserCountry')) { - $menu->addVisitorsItem('UserCountryMap_RealTimeMap', - $this->urlForAction('realtimeWorldMap'), - $order = 70); - } - } -} diff --git a/plugins/UserCountryMap/UserCountryMap.php b/plugins/UserCountryMap/UserCountryMap.php index c7819f7f8c..6453cc7f29 100644 --- a/plugins/UserCountryMap/UserCountryMap.php +++ b/plugins/UserCountryMap/UserCountryMap.php @@ -10,8 +10,9 @@ namespace Piwik\Plugins\UserCountryMap; use Piwik\FrontController; use Piwik\Piwik; +use Piwik\Widget\WidgetConfig; use Piwik\Version; -use Piwik\WidgetsList; +use Piwik\Widget\WidgetsList; use Piwik\Plugin\Manager as PluginManager; /** @@ -34,23 +35,11 @@ class UserCountryMap extends \Piwik\Plugin $hooks = array( 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', - 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', - 'Platform.initialized' => array( - 'after' => true, - 'function' => 'registerWidgets' - ) + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys' ); return $hooks; } - public function registerWidgets() - { - if (PluginManager::getInstance()->isPluginActivated('UserCountry')) { - WidgetsList::add('General_Visitors', Piwik::translate('UserCountryMap_VisitorMap'), 'UserCountryMap', 'visitorMap'); - WidgetsList::add('Live!', Piwik::translate('UserCountryMap_RealTimeMap'), 'UserCountryMap', 'realtimeMap'); - } - } - public function getJsFiles(&$jsFiles) { $jsFiles[] = "libs/bower_components/visibilityjs/lib/visibility.core.js"; diff --git a/plugins/UserCountryMap/Widgets/GetRealtimeMap.php b/plugins/UserCountryMap/Widgets/GetRealtimeMap.php new file mode 100644 index 0000000000..d6476bfe2d --- /dev/null +++ b/plugins/UserCountryMap/Widgets/GetRealtimeMap.php @@ -0,0 +1,29 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\UserCountryMap\Widgets; + +use Piwik\Widget\WidgetConfig; +use Piwik\Plugin\Manager as PluginManager; + +class GetRealtimeMap extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('General_Visitors'); + $config->setSubcategoryId('UserCountryMap_RealTimeMap'); + $config->setName('UserCountryMap_RealTimeMap'); + $config->setModule('UserCountryMap'); + $config->setAction('realtimeMap'); + $config->setOrder(5); + + if (!PluginManager::getInstance()->isPluginActivated('UserCountry')) { + $config->disable(); + } + } +} diff --git a/plugins/UserCountryMap/Widgets/GetVisitorMap.php b/plugins/UserCountryMap/Widgets/GetVisitorMap.php new file mode 100644 index 0000000000..930630df55 --- /dev/null +++ b/plugins/UserCountryMap/Widgets/GetVisitorMap.php @@ -0,0 +1,23 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\UserCountryMap\Widgets; + +use Piwik\Widget\WidgetConfig; + +class GetVisitorMap extends \Piwik\Widget\Widget +{ + public static function configure(WidgetConfig $config) + { + $config->setCategoryId('General_Visitors'); + $config->setSubcategoryId('UserCountry_SubmenuLocations'); + $config->setName('UserCountryMap_VisitorMap'); + $config->setAction('visitorMap'); + $config->setOrder(1); + } +} diff --git a/plugins/UserCountryMap/templates/visitorMap.twig b/plugins/UserCountryMap/templates/visitorMap.twig index 7873200c9f..b5445a2cc6 100644 --- a/plugins/UserCountryMap/templates/visitorMap.twig +++ b/plugins/UserCountryMap/templates/visitorMap.twig @@ -86,7 +86,7 @@ if ($('#dashboardWidgetsArea').length) { // dashboard mode - var $widgetContent = $('.UserCountryMap').parents('.widgetContent'); + var $widgetContent = $('.UserCountryMap').parents('.widgetContent').first(); $widgetContent.on('widget:create',function (evt, widget) { visitorMap = new UserCountryMap.VisitorMap(config, widget); diff --git a/plugins/UserLanguage/Reports/Base.php b/plugins/UserLanguage/Reports/Base.php index 366521b16a..68e8a91b4b 100644 --- a/plugins/UserLanguage/Reports/Base.php +++ b/plugins/UserLanguage/Reports/Base.php @@ -15,6 +15,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_VisitorSettings'; + $this->categoryId = 'General_Visitors'; + $this->subcategoryId = 'UserCountry_SubmenuLocations'; } } diff --git a/plugins/UserLanguage/Reports/GetLanguage.php b/plugins/UserLanguage/Reports/GetLanguage.php index f5babd65b8..8db3629134 100644 --- a/plugins/UserLanguage/Reports/GetLanguage.php +++ b/plugins/UserLanguage/Reports/GetLanguage.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\UserLanguage\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\UserLanguage\Columns\Language; +use Piwik\Plugin\Reports; class GetLanguage extends Base { @@ -20,8 +21,7 @@ class GetLanguage extends Base $this->dimension = new Language(); $this->name = Piwik::translate('UserLanguage_BrowserLanguage'); $this->documentation = ''; // TODO - $this->order = 10; - $this->widgetTitle = 'UserLanguage_BrowserLanguage'; + $this->order = 8; } public function configureView(ViewDataTable $view) @@ -37,7 +37,7 @@ class GetLanguage extends Base public function getRelatedReports() { return array( - self::factory('UserLanguage', 'getLanguageCode'), + Reports::factory('UserLanguage', 'getLanguageCode'), ); } diff --git a/plugins/UserLanguage/Reports/GetLanguageCode.php b/plugins/UserLanguage/Reports/GetLanguageCode.php index 0ca2c64bb2..9d9a46f201 100644 --- a/plugins/UserLanguage/Reports/GetLanguageCode.php +++ b/plugins/UserLanguage/Reports/GetLanguageCode.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\UserLanguage\Reports; use Piwik\Piwik; use Piwik\Plugins\UserLanguage\Columns\Language; +use Piwik\Plugin\Reports; class GetLanguageCode extends GetLanguage { @@ -20,13 +21,12 @@ class GetLanguageCode extends GetLanguage $this->name = Piwik::translate('UserLanguage_LanguageCode'); $this->documentation = ''; $this->order = 11; - $this->widgetTitle = 'UserLanguage_LanguageCode'; } public function getRelatedReports() { return array( - self::factory('UserLanguage', 'getLanguage'), + Reports::factory('UserLanguage', 'getLanguage'), ); } diff --git a/plugins/VisitFrequency/Controller.php b/plugins/VisitFrequency/Controller.php index e1279206fd..8e6af9cffd 100644 --- a/plugins/VisitFrequency/Controller.php +++ b/plugins/VisitFrequency/Controller.php @@ -8,9 +8,10 @@ */ namespace Piwik\Plugins\VisitFrequency; -use Piwik\API\Request; use Piwik\Common; +use Piwik\FrontController; use Piwik\Piwik; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; use Piwik\Translation\Translator; use Piwik\View; @@ -28,31 +29,22 @@ class Controller extends \Piwik\Plugin\Controller parent::__construct(); } - public function index() - { - $view = new View('@VisitFrequency/index'); - $this->setGeneralVariablesView($view); - - $view->graphEvolutionVisitFrequency = $this->getEvolutionGraph(array(), array('nb_visits_returning')); - $this->setSparklinesAndNumbers($view); - - return $view->render(); - } - + /** + * @deprecated used to be a widgetized URL. There to not break widget URLs + */ public function getSparklines() { - $view = new View('@VisitFrequency/getSparklines'); - $this->setSparklinesAndNumbers($view); - return $view->render(); + $_GET['forceView'] = '1'; + $_GET['viewDataTable'] = Sparklines::ID; + + return FrontController::getInstance()->fetchDispatch('VisitFrequency', 'get'); } - public function getEvolutionGraph(array $columns = array(), array $defaultColumns = array()) + public function getEvolutionGraph() { - if (empty($columns)) { - $columns = Common::getRequestVar('columns', false); - if (false !== $columns) { - $columns = Piwik::getArrayFromApiParameter($columns); - } + $columns = Common::getRequestVar('columns', false); + if (false !== $columns) { + $columns = Piwik::getArrayFromApiParameter($columns); } $documentation = $this->translator->translate('VisitFrequency_ReturningVisitsDocumentation') . '<br />' @@ -89,35 +81,10 @@ class Controller extends \Piwik\Plugin\Controller $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns, $selectableColumns, $documentation); - if (empty($view->config->columns_to_display) && !empty($defaultColumns)) { - $view->config->columns_to_display = $defaultColumns; + if (empty($view->config->columns_to_display)) { + $view->config->columns_to_display = array('nb_visits_returning'); } return $this->renderView($view); } - - protected function setSparklinesAndNumbers($view) - { - $view->urlSparklineNbVisitsReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_visits_returning'))); - $view->urlSparklineNbActionsReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions_returning'))); - $view->urlSparklineActionsPerVisitReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions_per_visit_returning'))); - $view->urlSparklineAvgVisitDurationReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_time_on_site_returning'))); - $view->urlSparklineBounceRateReturning = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('bounce_rate_returning'))); - - $dataTableFrequency = $this->getSummary(); - $dataRow = $dataTableFrequency->getFirstRow(); - $nbVisitsReturning = $dataRow->getColumn('nb_visits_returning'); - $view->nbVisitsReturning = $nbVisitsReturning; - $view->nbActionsReturning = $dataRow->getColumn('nb_actions_returning'); - $view->nbActionsPerVisitReturning = $dataRow->getColumn('nb_actions_per_visit_returning'); - $view->avgVisitDurationReturning = $dataRow->getColumn('avg_time_on_site_returning'); - $view->bounceRateReturning = $dataRow->getColumn('bounce_rate_returning'); - } - - protected function getSummary() - { - $requestString = "method=VisitFrequency.get&format=original"; - $request = new Request($requestString); - return $request->process(); - } } diff --git a/plugins/VisitFrequency/Menu.php b/plugins/VisitFrequency/Menu.php deleted file mode 100644 index e858bd2f76..0000000000 --- a/plugins/VisitFrequency/Menu.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitFrequency; - -use Piwik\Menu\MenuReporting; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addVisitorsItem('VisitFrequency_SubmenuFrequency', $this->urlForAction('index'), $order = 55); - } -} diff --git a/plugins/VisitFrequency/Reports/Get.php b/plugins/VisitFrequency/Reports/Get.php index eb4c7ddfbe..735f1cbe0f 100644 --- a/plugins/VisitFrequency/Reports/Get.php +++ b/plugins/VisitFrequency/Reports/Get.php @@ -9,17 +9,22 @@ namespace Piwik\Plugins\VisitFrequency\Reports; use Piwik\Piwik; +use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreHome\Columns\Metrics\ActionsPerVisit; use Piwik\Plugins\CoreHome\Columns\Metrics\AverageTimeOnSite; use Piwik\Plugins\CoreHome\Columns\Metrics\BounceRate; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; use Piwik\Plugins\VisitFrequency\Columns\Metrics\ReturningMetric; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class Get extends \Piwik\Plugin\Report { protected function init() { parent::init(); - $this->category = 'General_Visitors'; + $this->categoryId = 'General_Visitors'; $this->name = Piwik::translate('VisitFrequency_ColumnReturningVisits'); $this->documentation = ''; // TODO $this->processedMetrics = array( @@ -35,5 +40,60 @@ class Get extends \Piwik\Plugin\Report 'max_actions_returning' ); $this->order = 40; + $this->subcategoryId = 'VisitorInterest_Engagement'; } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('VisitFrequency_WidgetGraphReturning') + ->forceViewDataTable(Evolution::ID) + ->setAction('getEvolutionGraph') + ->setOrder(1) + ); + + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->forceViewDataTable(Sparklines::ID) + ->setName('VisitFrequency_WidgetOverview') + ->setOrder(2) + ); + } + + public function configureView(ViewDataTable $view) + { + if ($view->isViewDataTableId(Sparklines::ID)) { + $view->requestConfig->apiMethodToRequestDataTable = 'VisitFrequency.get'; + $this->addSparklineColumns($view); + $view->config->addTranslations($this->getSparklineTranslations()); + } + } + + private function getSparklineTranslations() + { + $translations = array( + 'nb_visits_returning' => 'ReturnVisits', + 'nb_actions_returning' => 'ReturnActions', + 'nb_actions_per_visit_returning' => 'ReturnAvgActions', + 'avg_time_on_site_returning' => 'ReturnAverageVisitDuration', + 'bounce_rate_returning' => 'ReturnBounceRate', + ); + + foreach ($translations as $metric => $key) { + $translations[$metric] = Piwik::translate('VisitFrequency_' . $key); + } + + return $translations; + } + + private function addSparklineColumns(Sparklines $view) + { + $view->config->addSparklineMetric(array('nb_visits_returning')); + $view->config->addSparklineMetric(array('avg_time_on_site_returning')); + $view->config->addSparklineMetric(array('nb_actions_per_visit_returning')); + $view->config->addSparklineMetric(array('bounce_rate_returning')); + $view->config->addSparklineMetric(array('nb_actions_returning')); + } + } diff --git a/plugins/VisitFrequency/Widgets.php b/plugins/VisitFrequency/Widgets.php deleted file mode 100644 index 53a5ddcd47..0000000000 --- a/plugins/VisitFrequency/Widgets.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitFrequency; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'General_Visitors'; - - public function init() - { - $this->addWidget('VisitFrequency_WidgetOverview', 'getSparklines'); - $this->addWidget('VisitFrequency_WidgetGraphReturning', - 'getEvolutionGraph', - array('columns' => array('nb_visits_returning'))); - } - -} diff --git a/plugins/VisitFrequency/lang/en.json b/plugins/VisitFrequency/lang/en.json index d157e0a88c..39b1879852 100644 --- a/plugins/VisitFrequency/lang/en.json +++ b/plugins/VisitFrequency/lang/en.json @@ -12,13 +12,13 @@ "ColumnUniqueReturningVisitors": "Unique returning visitors", "ColumnReturningUsers": "Returning Users", "PluginDescription": "Reports metrics about your first time new visitors and returning visitors.", - "ReturnActions": "%s actions by the returning visits", - "ReturnAverageVisitDuration": "%s average visit duration for returning visitors", - "ReturnAvgActions": "%s actions per returning visit", - "ReturnBounceRate": "%s returning visits have bounced (left the website after one page)", + "ReturnActions": "actions by the returning visits", + "ReturnAverageVisitDuration": "average visit duration for returning visitors", + "ReturnAvgActions": "actions per returning visit", + "ReturnBounceRate": "returning visits have bounced (left the website after one page)", "ReturningVisitDocumentation": "A returning visit is (as opposed to a new visit) made by someone who has visited the website at least once before.", "ReturningVisitsDocumentation": "This is an overview of the returning visits.", - "ReturnVisits": "%s returning visits", + "ReturnVisits": "returning visits", "SubmenuFrequency": "Frequency", "WidgetGraphReturning": "Returning Visits Over Time", "WidgetOverview": "Frequency Overview" diff --git a/plugins/VisitFrequency/templates/_sparklines.twig b/plugins/VisitFrequency/templates/_sparklines.twig deleted file mode 100644 index c6c123c5e2..0000000000 --- a/plugins/VisitFrequency/templates/_sparklines.twig +++ /dev/null @@ -1,39 +0,0 @@ - -{% if not isWidget %} -<div class="row"> - <div class="col-md-6"> -{% endif %} - - <div class="sparkline"> - {{ sparkline(urlSparklineNbVisitsReturning) }} - {{ 'VisitFrequency_ReturnVisits'|translate("<strong>"~nbVisitsReturning~"</strong>")|raw }} - </div> - <div class="sparkline"> - {{ sparkline(urlSparklineNbActionsReturning) }} - {{ 'VisitFrequency_ReturnActions'|translate("<strong>"~nbActionsReturning~"</strong>")|raw }} - </div> - <div class="sparkline"> - {{ sparkline(urlSparklineActionsPerVisitReturning) }} - {{ 'VisitFrequency_ReturnAvgActions'|translate("<strong>"~nbActionsPerVisitReturning~"</strong>")|raw }} - </div> - - {% if not isWidget %} - </div> - <div class="col-md-6"> - {% endif %} - - <div class="sparkline"> - {{ sparkline(urlSparklineAvgVisitDurationReturning) }} - {% set avgVisitDurationReturning=avgVisitDurationReturning|sumtime %} - {{ 'VisitFrequency_ReturnAverageVisitDuration'|translate("<strong>"~avgVisitDurationReturning~"</strong>")|raw }} - </div> - <div class="sparkline"> - {{ sparkline(urlSparklineBounceRateReturning) }} - {{ 'VisitFrequency_ReturnBounceRate'|translate("<strong>"~bounceRateReturning~"</strong>")|raw }} - </div> - {% include "_sparklineFooter.twig" %} - -{% if not isWidget %} - </div> -</div> -{% endif %} diff --git a/plugins/VisitFrequency/templates/getSparklines.twig b/plugins/VisitFrequency/templates/getSparklines.twig deleted file mode 100644 index fd158c8329..0000000000 --- a/plugins/VisitFrequency/templates/getSparklines.twig +++ /dev/null @@ -1 +0,0 @@ -{% include "@VisitFrequency/_sparklines.twig" %}
\ No newline at end of file diff --git a/plugins/VisitFrequency/templates/index.twig b/plugins/VisitFrequency/templates/index.twig deleted file mode 100644 index 6abd255b17..0000000000 --- a/plugins/VisitFrequency/templates/index.twig +++ /dev/null @@ -1,9 +0,0 @@ -{{ postEvent("Template.headerVisitsFrequency") }} - -<h2 piwik-enriched-headline data-graph-id="VisitFrequency.getEvolutionGraph">{{ 'VisitFrequency_ColumnReturningVisits'|translate }}</h2> - {{ graphEvolutionVisitFrequency|raw }} -<br/> - -{% include "@VisitFrequency/_sparklines.twig" %} - -{{ postEvent("Template.footerVisitsFrequency") }} diff --git a/plugins/VisitTime/Categories/TimesSubcategory.php b/plugins/VisitTime/Categories/TimesSubcategory.php new file mode 100644 index 0000000000..220a568ad3 --- /dev/null +++ b/plugins/VisitTime/Categories/TimesSubcategory.php @@ -0,0 +1,19 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\VisitTime\Categories; + +use Piwik\Category\Subcategory; + +class TimesSubcategory extends Subcategory +{ + protected $categoryId = 'General_Visitors'; + protected $id = 'VisitTime_SubmenuTimes'; + protected $order = 35; + +} diff --git a/plugins/VisitTime/Controller.php b/plugins/VisitTime/Controller.php deleted file mode 100644 index 08af238213..0000000000 --- a/plugins/VisitTime/Controller.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitTime; - -use Piwik\View; - -/** - * - */ -class Controller extends \Piwik\Plugin\Controller -{ - public function index() - { - $view = new View('@VisitTime/index'); - $view->dataTableVisitInformationPerLocalTime = $this->renderReport('getVisitInformationPerLocalTime'); - $view->dataTableVisitInformationPerServerTime = $this->renderReport('getVisitInformationPerServerTime'); - return $view->render(); - } -} diff --git a/plugins/VisitTime/Menu.php b/plugins/VisitTime/Menu.php deleted file mode 100644 index ffa3a5b015..0000000000 --- a/plugins/VisitTime/Menu.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitTime; - -use Piwik\Menu\MenuReporting; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addVisitorsItem('VisitTime_SubmenuTimes', $this->urlForAction('index'), $order = 65); - } -} diff --git a/plugins/VisitTime/Reports/Base.php b/plugins/VisitTime/Reports/Base.php index 89b553cd44..2dfbf0e169 100644 --- a/plugins/VisitTime/Reports/Base.php +++ b/plugins/VisitTime/Reports/Base.php @@ -15,7 +15,7 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'VisitsSummary_VisitsSummary'; + $this->categoryId = 'General_Visitors'; } public function getDefaultTypeViewDataTable() diff --git a/plugins/VisitTime/Reports/GetByDayOfWeek.php b/plugins/VisitTime/Reports/GetByDayOfWeek.php index d9a3a58ee8..a93c6f21ee 100644 --- a/plugins/VisitTime/Reports/GetByDayOfWeek.php +++ b/plugins/VisitTime/Reports/GetByDayOfWeek.php @@ -14,6 +14,7 @@ use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; use Piwik\Plugins\VisitTime\Columns\DayOfTheWeek; use Piwik\Period; +use Piwik\Plugin\Reports; use Piwik\Site; class GetByDayOfWeek extends Base @@ -28,7 +29,7 @@ class GetByDayOfWeek extends Base $this->documentation = Piwik::translate('VisitTime_WidgetByDayOfWeekDocumentation'); $this->constantRowsCount = true; $this->order = 25; - $this->widgetTitle = 'VisitTime_VisitsByDayOfWeek'; + $this->subcategoryId = 'VisitTime_SubmenuTimes'; } public function configureView(ViewDataTable $view) @@ -73,4 +74,11 @@ class GetByDayOfWeek extends Base } return $dateRange; } + + public function getRelatedReports() + { + return array( + Reports::factory('VisitTime', 'getVisitInformationPerLocalTime') + ); + } } diff --git a/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php b/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php index 6546463cd1..9372875619 100644 --- a/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php +++ b/plugins/VisitTime/Reports/GetVisitInformationPerLocalTime.php @@ -13,6 +13,7 @@ use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; use Piwik\Plugins\VisitTime\Columns\LocalTime; +use Piwik\Plugin\Reports; class GetVisitInformationPerLocalTime extends Base { @@ -23,11 +24,12 @@ class GetVisitInformationPerLocalTime extends Base { parent::init(); $this->dimension = new LocalTime(); - $this->name = Piwik::translate('VisitTime_WidgetLocalTime'); + $this->name = Piwik::translate('VisitTime_LocalTime'); $this->documentation = Piwik::translate('VisitTime_WidgetLocalTimeDocumentation', array('<strong>', '</strong>')); $this->constantRowsCount = true; - $this->order = 20; - $this->widgetTitle = 'VisitTime_WidgetLocalTime'; + $this->order = 15; + + $this->subcategoryId = 'VisitTime_SubmenuTimes'; } public function configureView(ViewDataTable $view) @@ -42,10 +44,12 @@ class GetVisitInformationPerLocalTime extends Base if ($view->isViewDataTableId(Graph::ID)) { $view->config->max_graph_elements = false; } + } - // add the visits by day of week as a related report, if the current period is not 'day' - if (Common::getRequestVar('period', 'day') != 'day') { - $view->config->addRelatedReport('VisitTime.getByDayOfWeek', Piwik::translate('VisitTime_VisitsByDayOfWeek')); - } + public function getRelatedReports() + { + return array( + Reports::factory('VisitTime', 'getByDayOfWeek') + ); } } diff --git a/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php b/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php index ce213fb211..871f760046 100644 --- a/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php +++ b/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php @@ -22,12 +22,13 @@ class GetVisitInformationPerServerTime extends Base { parent::init(); $this->dimension = new ServerTime(); - $this->name = Piwik::translate('VisitTime_WidgetServerTime'); + $this->name = Piwik::translate('VisitTime_ServerTime'); $this->documentation = Piwik::translate('VisitTime_WidgetServerTimeDocumentation', array('<strong>', '</strong>')); $this->constantRowsCount = true; $this->hasGoalMetrics = true; - $this->order = 15; - $this->widgetTitle = 'VisitTime_WidgetServerTime'; + $this->order = 20; + + $this->subcategoryId = 'VisitTime_SubmenuTimes'; } public function configureView(ViewDataTable $view) diff --git a/plugins/VisitTime/templates/index.twig b/plugins/VisitTime/templates/index.twig deleted file mode 100644 index 688cde0338..0000000000 --- a/plugins/VisitTime/templates/index.twig +++ /dev/null @@ -1,13 +0,0 @@ -<div class="row"> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'VisitTime_LocalTime'|translate }}</h2> - {{ dataTableVisitInformationPerLocalTime|raw }} - </div> - - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'VisitTime_ServerTime'|translate }}</h2> - {{ dataTableVisitInformationPerServerTime|raw }} - </div> - -</div> diff --git a/plugins/VisitorInterest/Controller.php b/plugins/VisitorInterest/Controller.php deleted file mode 100644 index 8cb5200c4d..0000000000 --- a/plugins/VisitorInterest/Controller.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitorInterest; - -use Piwik\View; - -class Controller extends \Piwik\Plugin\Controller -{ - public function index() - { - $view = new View('@VisitorInterest/index'); - $view->dataTableNumberOfVisitsPerVisitDuration = $this->renderReport('getNumberOfVisitsPerVisitDuration'); - $view->dataTableNumberOfVisitsPerPage = $this->renderReport('getNumberOfVisitsPerPage'); - $view->dataTableNumberOfVisitsByVisitNum = $this->renderReport('getNumberOfVisitsByVisitCount'); - $view->dataTableNumberOfVisitsByDaysSinceLast = $this->renderReport('getNumberOfVisitsByDaysSinceLast'); - return $view->render(); - } -}
\ No newline at end of file diff --git a/plugins/VisitorInterest/Menu.php b/plugins/VisitorInterest/Menu.php deleted file mode 100644 index 17de395000..0000000000 --- a/plugins/VisitorInterest/Menu.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitorInterest; - -use Piwik\Menu\MenuReporting; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->rename('General_Visitors', 'VisitFrequency_SubmenuFrequency', - 'General_Visitors', 'VisitorInterest_Engagement'); - } -} diff --git a/plugins/VisitorInterest/Reports/Base.php b/plugins/VisitorInterest/Reports/Base.php index fb3f0b4a42..3381a8e805 100644 --- a/plugins/VisitorInterest/Reports/Base.php +++ b/plugins/VisitorInterest/Reports/Base.php @@ -12,7 +12,8 @@ abstract class Base extends \Piwik\Plugin\Report { protected function init() { - $this->category = 'General_Visitors'; + $this->categoryId = 'General_Visitors'; + $this->subcategoryId = 'VisitorInterest_Engagement'; } } diff --git a/plugins/VisitorInterest/Reports/GetNumberOfVisitsByDaysSinceLast.php b/plugins/VisitorInterest/Reports/GetNumberOfVisitsByDaysSinceLast.php index fd935d47c6..b7b7302842 100644 --- a/plugins/VisitorInterest/Reports/GetNumberOfVisitsByDaysSinceLast.php +++ b/plugins/VisitorInterest/Reports/GetNumberOfVisitsByDaysSinceLast.php @@ -11,6 +11,8 @@ namespace Piwik\Plugins\VisitorInterest\Reports; use Piwik\Piwik; use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\VisitorInterest\Columns\VisitsByDaysSinceLastVisit; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetNumberOfVisitsByDaysSinceLast extends Base { @@ -26,7 +28,14 @@ class GetNumberOfVisitsByDaysSinceLast extends Base $this->processedMetrics = false; $this->constantRowsCount = true; $this->order = 30; - $this->widgetTitle = 'VisitorInterest_WidgetVisitsByDaysSinceLast'; + + $this->subcategoryId = 'VisitorInterest_Engagement'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widget = $factory->createWidget()->setName('VisitorInterest_WidgetVisitsByDaysSinceLast'); + $widgetsList->addWidgetConfig($widget); } public function configureView(ViewDataTable $view) diff --git a/plugins/VisitorInterest/Reports/GetNumberOfVisitsByVisitCount.php b/plugins/VisitorInterest/Reports/GetNumberOfVisitsByVisitCount.php index 74dbf69c14..d7c2a7b7e8 100644 --- a/plugins/VisitorInterest/Reports/GetNumberOfVisitsByVisitCount.php +++ b/plugins/VisitorInterest/Reports/GetNumberOfVisitsByVisitCount.php @@ -31,7 +31,6 @@ class GetNumberOfVisitsByVisitCount extends Base ); $this->constantRowsCount = true; $this->order = 25; - $this->widgetTitle = 'VisitorInterest_visitsByVisitCount'; } public function configureView(ViewDataTable $view) diff --git a/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerPage.php b/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerPage.php index de15ed5f5f..395c70da2b 100644 --- a/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerPage.php +++ b/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerPage.php @@ -13,6 +13,8 @@ use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Cloud; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; use Piwik\Plugins\VisitorInterest\Columns\PagesPerVisit; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetNumberOfVisitsPerPage extends Base { @@ -29,7 +31,13 @@ class GetNumberOfVisitsPerPage extends Base $this->processedMetrics = false; $this->constantRowsCount = true; $this->order = 20; - $this->widgetTitle = 'VisitorInterest_WidgetPages'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget()->setName('VisitorInterest_VisitsPerNbOfPages') + ); } public function getDefaultTypeViewDataTable() diff --git a/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerVisitDuration.php b/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerVisitDuration.php index ead1849b7f..8ca31b560c 100644 --- a/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerVisitDuration.php +++ b/plugins/VisitorInterest/Reports/GetNumberOfVisitsPerVisitDuration.php @@ -13,6 +13,8 @@ use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreVisualizations\Visualizations\Cloud; use Piwik\Plugins\CoreVisualizations\Visualizations\Graph; use Piwik\Plugins\VisitorInterest\Columns\VisitDuration; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; class GetNumberOfVisitsPerVisitDuration extends Base { @@ -29,7 +31,13 @@ class GetNumberOfVisitsPerVisitDuration extends Base $this->processedMetrics = false; $this->constantRowsCount = true; $this->order = 15; - $this->widgetTitle = 'VisitorInterest_WidgetLengths'; + } + + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget()->setName('VisitorInterest_VisitsPerDuration') + ); } public function getDefaultTypeViewDataTable() diff --git a/plugins/VisitorInterest/templates/index.twig b/plugins/VisitorInterest/templates/index.twig deleted file mode 100644 index 332289e106..0000000000 --- a/plugins/VisitorInterest/templates/index.twig +++ /dev/null @@ -1,21 +0,0 @@ -<div class="row"> - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'VisitorInterest_VisitsPerDuration'|translate }}</h2> - {{ dataTableNumberOfVisitsPerVisitDuration|raw }} - </div> - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'VisitorInterest_VisitsPerNbOfPages'|translate }}</h2> - {{ dataTableNumberOfVisitsPerPage|raw }} - </div> -</div> - -<div class="row"> - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'VisitorInterest_visitsByVisitCount'|translate }}</h2> - {{ dataTableNumberOfVisitsByVisitNum|raw }} - </div> - <div class="col-md-6"> - <h2 piwik-enriched-headline>{{ 'VisitorInterest_VisitsByDaysSinceLast'|translate }}</h2> - {{ dataTableNumberOfVisitsByDaysSinceLast|raw }} - </div> -</div> diff --git a/plugins/VisitsSummary/API.php b/plugins/VisitsSummary/API.php index b80c505fbb..c46bd93b7b 100644 --- a/plugins/VisitsSummary/API.php +++ b/plugins/VisitsSummary/API.php @@ -12,6 +12,7 @@ use Piwik\Archive; use Piwik\Metrics\Formatter; use Piwik\Piwik; use Piwik\Plugin\Report; +use Piwik\Plugin\Reports; use Piwik\SettingsPiwik; /** @@ -29,7 +30,7 @@ class API extends \Piwik\Plugin\API $requestedColumns = Piwik::getArrayFromApiParameter($columns); - $report = Report::factory("VisitsSummary", "get"); + $report = Reports::factory("VisitsSummary", "get"); $columns = $report->getMetricsRequiredForReport($this->getCoreColumns($period), $requestedColumns); $dataTable = $archive->getDataTableFromNumeric($columns); diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php index 336ee26937..ca141b7ed8 100644 --- a/plugins/VisitsSummary/Controller.php +++ b/plugins/VisitsSummary/Controller.php @@ -12,8 +12,9 @@ use Piwik\API\Request; use Piwik\Common; use Piwik\DataTable; use Piwik\DataTable\Row; +use Piwik\FrontController; use Piwik\Piwik; -use Piwik\Plugins\Actions\API as APIActions; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; use Piwik\Site; use Piwik\Translation\Translator; use Piwik\View; @@ -35,38 +36,32 @@ class Controller extends \Piwik\Plugin\Controller parent::__construct(); } - public function index() + /** + * @deprecated used to be a widgetized URL. There to not break widget URLs + */ + public function getSparklines() { - $view = new View('@VisitsSummary/index'); - $this->setPeriodVariablesView($view); - $view->graphEvolutionVisitsSummary = $this->getEvolutionGraph(array(), array('nb_visits'), 'getIndexGraph'); - $this->setSparklinesAndNumbers($view); - return $view->render(); - } + $_GET['forceView'] = '1'; + $_GET['viewDataTable'] = Sparklines::ID; - // sparkline.js:81 dataTable.trigger('reload', …); does not remove the old headline, - // so when updating this graph (such as when selecting a different metric) - // ONLY the graph should be returned - public function getIndexGraph() - { - return $this->getEvolutionGraph(array(), array(), __FUNCTION__); + return FrontController::getInstance()->fetchDispatch('VisitsSummary', 'get'); } - public function getSparklines() + /** + * @deprecated used to be a widgetized URL. There to not break widget URLs + */ + public function index() { - $view = new View('@VisitsSummary/getSparklines'); - $this->setPeriodVariablesView($view); - $this->setSparklinesAndNumbers($view); - return $view->render(); + $_GET['containerId'] = 'VisitOverviewWithGraph'; + + return FrontController::getInstance()->fetchDispatch('CoreHome', 'renderWidgetContainer'); } - public function getEvolutionGraph(array $columns = array(), array $defaultColumns = array(), $callingAction = __FUNCTION__) + public function getEvolutionGraph() { - if (empty($columns)) { - $columns = Common::getRequestVar('columns', false); - if (false !== $columns) { - $columns = Piwik::getArrayFromApiParameter($columns); - } + $columns = Common::getRequestVar('columns', false); + if (false !== $columns) { + $columns = Piwik::getArrayFromApiParameter($columns); } $documentation = $this->translator->translate('VisitsSummary_VisitsSummaryDocumentation') . '<br />' @@ -116,11 +111,11 @@ class Controller extends \Piwik\Plugin\Controller } // $callingAction may be specified to distinguish between // "VisitsSummary_WidgetLastVisits" and "VisitsSummary_WidgetOverviewGraph" - $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, $callingAction, $columns, + $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns, $selectableColumns, $documentation); - if (empty($view->config->columns_to_display) && !empty($defaultColumns)) { - $view->config->columns_to_display = $defaultColumns; + if (empty($view->config->columns_to_display)) { + $view->config->columns_to_display = array('nb_visits'); } return $this->renderView($view); @@ -137,87 +132,4 @@ class Controller extends \Piwik\Plugin\Controller return empty($result) ? new DataTable() : $result; } - - public static function getVisits() - { - $requestString = "method=VisitsSummary.getVisits" . - "&format=original" . - "&disable_generic_filters=1"; - $request = new Request($requestString); - return $request->process(); - } - - protected function setSparklinesAndNumbers($view) - { - $view->urlSparklineNbVisits = $this->getUrlSparkline('getEvolutionGraph', array('columns' => $view->displayUniqueVisitors ? array('nb_visits', 'nb_uniq_visitors') : array('nb_visits'))); - $view->urlSparklineNbUsers = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_users'))); - $view->urlSparklineNbPageviews = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_pageviews', 'nb_uniq_pageviews'))); - $view->urlSparklineNbDownloads = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_downloads', 'nb_uniq_downloads'))); - $view->urlSparklineNbOutlinks = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_outlinks', 'nb_uniq_outlinks'))); - $view->urlSparklineAvgVisitDuration = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_time_on_site'))); - $view->urlSparklineMaxActions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('max_actions'))); - $view->urlSparklineActionsPerVisit = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions_per_visit'))); - $view->urlSparklineBounceRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('bounce_rate'))); - $view->urlSparklineAvgGenerationTime = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_time_generation'))); - - $idSite = Common::getRequestVar('idSite'); - $displaySiteSearch = Site::isSiteSearchEnabledFor($idSite); - if ($displaySiteSearch) { - $view->urlSparklineNbSearches = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_searches', 'nb_keywords'))); - } - $view->displaySiteSearch = $displaySiteSearch; - - $dataTableVisit = self::getVisitsSummary(); - $dataRow = $dataTableVisit->getRowsCount() == 0 ? new Row() : $dataTableVisit->getFirstRow(); - $view->nbUniqVisitors = (int)$dataRow->getColumn('nb_uniq_visitors'); - $view->nbUsers = (int)$dataRow->getColumn('nb_users'); - $nbVisits = (int)$dataRow->getColumn('nb_visits'); - $view->nbVisits = $nbVisits; - - $view->averageVisitDuration = $dataRow->getColumn('avg_time_on_site'); - $view->bounceRate = $dataRow->getColumn('bounce_rate'); - $view->maxActions = (int)$dataRow->getColumn('max_actions'); - $view->nbActionsPerVisit = $dataRow->getColumn('nb_actions_per_visit'); - - if (Common::isActionsPluginEnabled()) { - $view->showActionsPluginReports = true; - - $dataTableActions = Request::processRequest("Actions.get", array( - 'idSite' => $idSite, - 'period' => Common::getRequestVar('period'), - 'date' => Common::getRequestVar('date'), - 'segment' => Request::getRawSegmentFromRequest() - ), $defaultParams = array()); - - $dataActionsRow = - $dataTableActions->getRowsCount() == 0 ? new Row() : $dataTableActions->getFirstRow(); - - $view->nbPageviews = (int)$dataActionsRow->getColumn('nb_pageviews'); - $view->nbUniquePageviews = (int)$dataActionsRow->getColumn('nb_uniq_pageviews'); - $view->nbDownloads = (int)$dataActionsRow->getColumn('nb_downloads'); - $view->nbUniqueDownloads = (int)$dataActionsRow->getColumn('nb_uniq_downloads'); - $view->nbOutlinks = (int)$dataActionsRow->getColumn('nb_outlinks'); - $view->nbUniqueOutlinks = (int)$dataActionsRow->getColumn('nb_uniq_outlinks'); - $view->averageGenerationTime = $dataActionsRow->getColumn('avg_time_generation'); - - if ($displaySiteSearch) { - $view->nbSearches = (int)$dataActionsRow->getColumn('nb_searches'); - $view->nbKeywords = (int)$dataActionsRow->getColumn('nb_keywords'); - } - - // backward compatibility: - // show actions if the finer metrics are not archived - $view->showOnlyActions = false; - if ($dataActionsRow->getColumn('nb_pageviews') - + $dataActionsRow->getColumn('nb_downloads') - + $dataActionsRow->getColumn('nb_outlinks') == 0 - && $dataRow->getColumn('nb_actions') > 0 - ) { - $view->showOnlyActions = true; - $view->nbActions = $dataRow->getColumn('nb_actions'); - $view->urlSparklineNbActions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_actions'))); - } - } - - } } diff --git a/plugins/VisitsSummary/Menu.php b/plugins/VisitsSummary/Menu.php deleted file mode 100644 index 1b87e46be9..0000000000 --- a/plugins/VisitsSummary/Menu.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitsSummary; - -use Piwik\Menu\MenuReporting; - -class Menu extends \Piwik\Plugin\Menu -{ - public function configureReportingMenu(MenuReporting $menu) - { - $menu->addVisitorsItem('', array('module' => 'VisitsSummary', 'action' => 'index'), 10); - $menu->addVisitorsItem('General_Overview', array('module' => 'VisitsSummary', 'action' => 'index'), 1); - } -} diff --git a/plugins/VisitsSummary/Reports/Get.php b/plugins/VisitsSummary/Reports/Get.php index 55696873be..7fb95d2950 100644 --- a/plugins/VisitsSummary/Reports/Get.php +++ b/plugins/VisitsSummary/Reports/Get.php @@ -8,11 +8,23 @@ */ namespace Piwik\Plugins\VisitsSummary\Reports; +use Piwik\Common; +use Piwik\Container\StaticContainer; +use Piwik\DataTable; use Piwik\DataTable\DataTableInterface; +use Piwik\Metrics\Formatter; use Piwik\Piwik; +use Piwik\Plugin\ViewDataTable; use Piwik\Plugins\CoreHome\Columns\Metrics\ActionsPerVisit; use Piwik\Plugins\CoreHome\Columns\Metrics\AverageTimeOnSite; use Piwik\Plugins\CoreHome\Columns\Metrics\BounceRate; +use Piwik\Plugins\CoreHome\Columns\UserId; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\Report\ReportWidgetFactory; +use Piwik\SettingsPiwik; +use Piwik\Site; +use Piwik\Widget\WidgetsList; class Get extends \Piwik\Plugin\Report { @@ -21,7 +33,7 @@ class Get extends \Piwik\Plugin\Report protected function init() { parent::init(); - $this->category = 'VisitsSummary_VisitsSummary'; + $this->categoryId = 'General_Visitors'; $this->name = Piwik::translate('VisitsSummary_VisitsSummary'); $this->documentation = ''; // TODO $this->processedMetrics = array( @@ -36,12 +48,145 @@ class Get extends \Piwik\Plugin\Report 'nb_actions', 'max_actions' ); + $this->subcategoryId = 'General_Overview'; // Used to process metrics, not displayed/used directly // 'sum_visit_length', // 'nb_visits_converted', $this->order = 1; } + public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory) + { + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('VisitsSummary_WidgetLastVisits') + ->forceViewDataTable(Evolution::ID) + ->setAction('getEvolutionGraph') + ->setOrder(5) + ); + + $widgetsList->addWidgetConfig( + $factory->createWidget() + ->setName('VisitsSummary_WidgetVisits') + ->forceViewDataTable(Sparklines::ID) + ->setOrder(10) + ); + } + + public function configureView(ViewDataTable $view) + { + if ($view->isViewDataTableId(Sparklines::ID)) { + /** @var Sparklines $view */ + $view->requestConfig->apiMethodToRequestDataTable = 'API.get'; + $this->addSparklineColumns($view); + $view->config->addTranslations($this->getSparklineTranslations()); + $view->config->filters[] = function (DataTable $table) use ($view) { + $firstRow = $table->getFirstRow(); + + if (($firstRow->getColumn('nb_pageviews') + + $firstRow->getColumn('nb_downloads') + + $firstRow->getColumn('nb_outlinks')) == 0 + && $firstRow->getColumn('nb_actions') > 0) { + $view->config->removeSparklineMetric(array('nb_downloads', 'nb_uniq_downloads')); + $view->config->removeSparklineMetric(array('nb_outlinks', 'nb_uniq_outlinks')); + $view->config->removeSparklineMetric(array('nb_pageviews', 'nb_uniq_pageviews')); + $view->config->removeSparklineMetric(array('nb_searches', 'nb_keywords')); + } else { + $view->config->removeSparklineMetric(array('nb_actions')); + } + + $nbUsers = $firstRow->getColumn('nb_users'); + if (!is_numeric($nbUsers) || 0 >= $nbUsers) { + $view->config->replaceSparklineMetric(array('nb_users'), ''); + } + + $avgGenerationTime = $firstRow->getColumn('avg_time_generation'); + if (false === $avgGenerationTime) { + // fix avgGenerationTime is not formatted if value is false + /** @var Formatter $formatter */ + $formatter = StaticContainer::get('Piwik\Metrics\Formatter'); + $avgGenerationTime = $formatter->getPrettyTimeFromSeconds($avgGenerationTime, true); + $firstRow->setColumn('avg_time_generation', $avgGenerationTime); + } + }; + } + } + + private function getSparklineTranslations() + { + $translations = array( + 'nb_actions' => 'NbActionsDescription', + 'nb_visits' => 'NbVisitsDescription', + 'nb_users' => 'NbUsersDescription', + 'nb_uniq_visitors' => 'NbUniqueVisitors', + 'avg_time_generation' => 'AverageGenerationTime', + 'avg_time_on_site' => 'AverageVisitDuration', + 'max_actions' => 'MaxNbActions', + 'nb_actions_per_visit' => 'NbActionsPerVisit', + 'nb_downloads' => 'NbDownloadsDescription', + 'nb_uniq_downloads' => 'NbUniqueDownloadsDescription', + 'nb_outlinks' => 'NbOutlinksDescription', + 'nb_uniq_outlinks' => 'NbUniqueOutlinksDescription', + 'nb_keywords' => 'NbKeywordsDescription', + 'nb_searches' => 'NbSearchesDescription', + 'nb_pageviews' => 'NbPageviewsDescription', + 'nb_uniq_pageviews' => 'NbUniquePageviewsDescription', + 'bounce_rate' => 'NbVisitsBounced', + ); + + foreach ($translations as $metric => $key) { + $translations[$metric] = Piwik::translate('VisitsSummary_' . $key); + } + + return $translations; + } + + private function addSparklineColumns(Sparklines $view) + { + $currentPeriod = Common::getRequestVar('period'); + $currentIdSite = Common::getRequestVar('idSite'); + $currentDate = Common::getRequestVar('date'); + $displayUniqueVisitors = SettingsPiwik::isUniqueVisitorsEnabled($currentPeriod); + + $isActionPluginEnabled = Common::isActionsPluginEnabled(); + + $view->config->addSparklineMetric($displayUniqueVisitors ? array('nb_visits', 'nb_uniq_visitors') : array('nb_visits'), 5); + + if ($isActionPluginEnabled) { + $view->config->addSparklineMetric(array('nb_actions'), 10); // either actions or pageviews will be displayed + $view->config->addSparklineMetric(array('nb_pageviews', 'nb_uniq_pageviews'), 20); + } else { + // make sure to still create a div on the right side for this, just leave it empty + $view->config->addPlaceholder(10); + } + + $userId = new UserId(); + if ($userId->isUsedInAtLeastOneSite($currentIdSite, $currentPeriod, $currentDate)) { + $view->config->addSparklineMetric(array('nb_users'), 30); + $view->config->addPlaceholder(31); + } + + $view->config->addSparklineMetric(array('avg_time_on_site'), 40); + + $idSite = Common::getRequestVar('idSite'); + if ($isActionPluginEnabled && Site::isSiteSearchEnabledFor($idSite)) { + $view->config->addSparklineMetric(array('nb_searches', 'nb_keywords'), 50); + } else { + // make sure to still create a div on the right side for this, just leave it empty + $view->config->addPlaceholder(50); + } + + $view->config->addSparklineMetric(array('bounce_rate'), 60); + + if ($isActionPluginEnabled) { + $view->config->addSparklineMetric(array('nb_downloads', 'nb_uniq_downloads'), 70); + $view->config->addSparklineMetric(array('nb_actions_per_visit'), 71); + $view->config->addSparklineMetric(array('nb_outlinks', 'nb_uniq_outlinks'), 72); + $view->config->addSparklineMetric(array('avg_time_generation'), 73); + $view->config->addSparklineMetric(array('max_actions'), 74); + } + } + public function getMetrics() { $metrics = parent::getMetrics(); diff --git a/plugins/VisitsSummary/Widgets.php b/plugins/VisitsSummary/Widgets.php deleted file mode 100644 index 5ec7eef56e..0000000000 --- a/plugins/VisitsSummary/Widgets.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -/** - * Piwik - free/libre analytics platform - * - * @link http://piwik.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - * - */ -namespace Piwik\Plugins\VisitsSummary; - -class Widgets extends \Piwik\Plugin\Widgets -{ - protected $category = 'VisitsSummary_VisitsSummary'; - - public function init() - { - $this->addWidget('VisitsSummary_WidgetLastVisits', 'getEvolutionGraph', array('columns' => array('nb_visits'))); - $this->addWidget('VisitsSummary_WidgetVisits', 'getSparklines'); - $this->addWidget('VisitsSummary_WidgetOverviewGraph', 'index'); - } - -} diff --git a/plugins/VisitsSummary/Widgets/Index.php b/plugins/VisitsSummary/Widgets/Index.php new file mode 100644 index 0000000000..1b39fad7c6 --- /dev/null +++ b/plugins/VisitsSummary/Widgets/Index.php @@ -0,0 +1,47 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Plugins\VisitsSummary\Widgets; + +use Piwik\Plugin\Report; +use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution; +use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines; +use Piwik\Plugin\Reports; +use Piwik\Report\ReportWidgetFactory; +use Piwik\Widget\WidgetsList; + +class Index extends \Piwik\Widget\WidgetContainerConfig +{ + protected $categoryId = 'General_Visitors'; + protected $name = 'VisitsSummary_WidgetOverviewGraph'; + protected $id = 'VisitOverviewWithGraph'; + protected $isWidgetizable = true; + + public function isEnabled() + { + return Reports::factory('VisitsSummary', 'get')->isEnabled(); + } + + public function getWidgetConfigs() + { + $report = Reports::factory('VisitsSummary', 'get'); + + $factory = new ReportWidgetFactory($report); + $widgets = array(); + + $list = new WidgetsList(); + $report->configureWidgets($list, $factory); + + foreach ($list->getWidgetConfigs() as $config) { + $config->setIsNotWidgetizable(); + $widgets[] = $config; + } + + return $widgets; + } +} diff --git a/plugins/VisitsSummary/lang/en.json b/plugins/VisitsSummary/lang/en.json index e4a46bc5ae..fc9d3c4777 100644 --- a/plugins/VisitsSummary/lang/en.json +++ b/plugins/VisitsSummary/lang/en.json @@ -1,22 +1,24 @@ { "VisitsSummary": { - "AverageGenerationTime": "%s average generation time", - "AverageVisitDuration": "%s average visit duration", + "AverageGenerationTime": "average generation time", + "AverageVisitDuration": "average visit duration", "GenerateQueries": "%s queries executed", "GenerateTime": "%s seconds to generate the page", - "MaxNbActions": "%s max actions in one visit", + "MaxNbActions": "max actions in one visit", "NbActionsDescription": "%s actions", - "NbActionsPerVisit": "%s actions (page views, downloads, outlinks and internal site searches) per visit", - "NbDownloadsDescription": "%s downloads", - "NbKeywordsDescription": "%s unique keywords", - "NbOutlinksDescription": "%s outlinks", - "NbPageviewsDescription": "%s pageviews", - "NbSearchesDescription": "%s total searches on your website", - "NbUniqueDownloadsDescription": "%s unique downloads", - "NbUniqueOutlinksDescription": "%s unique outlinks", - "NbUniquePageviewsDescription": "%s unique pageviews", - "NbUniqueVisitors": "%s unique visitors", - "NbVisitsBounced": "%s visits have bounced (left the website after one page)", + "NbActionsPerVisit": "actions (page views, downloads, outlinks and internal site searches) per visit", + "NbDownloadsDescription": "downloads", + "NbKeywordsDescription": "unique keywords", + "NbOutlinksDescription": "outlinks", + "NbPageviewsDescription": "pageviews", + "NbSearchesDescription": "total searches on your website", + "NbUniqueDownloadsDescription": "unique downloads", + "NbUniqueOutlinksDescription": "unique outlinks", + "NbUniquePageviewsDescription": "unique pageviews", + "NbUniqueVisitors": "unique visitors", + "NbUsersDescription": "users", + "NbVisitsDescription": "visits", + "NbVisitsBounced": "visits have bounced (left the website after one page)", "PluginDescription": "Reports general analytics metrics: visits, unique visitors, number of actions, bounce rate, etc.", "VisitsSummary": "Visits Summary", "VisitsSummaryDocumentation": "This is an overview of the visit evolution.", diff --git a/plugins/Widgetize/Controller.php b/plugins/Widgetize/Controller.php index 2e2e5bf8cb..cf6cb5114a 100644 --- a/plugins/Widgetize/Controller.php +++ b/plugins/Widgetize/Controller.php @@ -12,7 +12,6 @@ use Piwik\API\Request; use Piwik\Common; use Piwik\FrontController; use Piwik\View; -use Piwik\WidgetsList; /** * @@ -22,7 +21,6 @@ class Controller extends \Piwik\Plugin\Controller public function index() { $view = new View('@Widgetize/index'); - $view->availableWidgets = json_encode(WidgetsList::get()); $this->setGeneralVariablesView($view); return $view->render(); } @@ -35,7 +33,7 @@ class Controller extends \Piwik\Plugin\Controller $controllerName = Common::getRequestVar('moduleToWidgetize'); $actionName = Common::getRequestVar('actionToWidgetize'); - if($controllerName == 'API') { + if ($controllerName == 'API') { throw new \Exception("Widgetizing API requests is not supported for security reasons. Please change query parameter 'moduleToWidgetize'."); } diff --git a/plugins/Widgetize/templates/iframe.twig b/plugins/Widgetize/templates/iframe.twig index ce40bdb940..892960316a 100644 --- a/plugins/Widgetize/templates/iframe.twig +++ b/plugins/Widgetize/templates/iframe.twig @@ -17,6 +17,7 @@ <!--[if (gte IE 9)|!(IE)]><!--> <body ng-app="app"> <![endif]--> + <div piwik-popover-handler></div> <div class="widget"> {{ content|raw }} </div> diff --git a/plugins/Widgetize/templates/index.twig b/plugins/Widgetize/templates/index.twig index 49a2b5f6c1..a798723928 100644 --- a/plugins/Widgetize/templates/index.twig +++ b/plugins/Widgetize/templates/index.twig @@ -24,7 +24,6 @@ $('#widgetPreview').widgetPreview({ onPreviewLoaded: widgetized.callbackAddExportButtonsUnderWidget }); - broadcast.init(); }); </script> diff --git a/plugins/Widgetize/tests/System/WidgetTest.php b/plugins/Widgetize/tests/System/WidgetTest.php index d2d6ec3260..2425ed3c9a 100644 --- a/plugins/Widgetize/tests/System/WidgetTest.php +++ b/plugins/Widgetize/tests/System/WidgetTest.php @@ -11,10 +11,11 @@ namespace Piwik\Plugins\Widgetize\tests\Integration; use Piwik\Container\StaticContainer; use Piwik\Http\ControllerResolver; use Piwik\Piwik; +use Piwik\Plugins\API; use Piwik\Plugins\Goals; use Piwik\Plugins\Widgetize\tests\Fixtures\WidgetizeFixture; use Piwik\Tests\Framework\TestCase\SystemTestCase; -use Piwik\WidgetsList; +use Piwik\Widget\WidgetsList; /** * @group Widgetize @@ -35,7 +36,7 @@ class WidgetTest extends SystemTestCase $_GET = array(); $_GET['idSite'] = self::$fixture->idSite; $_GET['period'] = 'year'; - $_GET['date'] = 'today'; + $_GET['date'] = '2013-01-23'; } public function tearDown() @@ -44,28 +45,35 @@ class WidgetTest extends SystemTestCase parent::tearDown(); } + public function test_allWidgetUniqueIdsAreActuallyUnique() + { + $uniqueIds = array(); + foreach (WidgetsList::get()->getWidgetConfigs() as $widget) { + $uniqueIds[] = $widget->getUniqueId(); + } + + $this->assertEquals(array_unique($uniqueIds), $uniqueIds); + } + public function test_AvailableWidgetListIsUpToDate() { - $namesOfWidgetsThatAreAPI = $this->getWidgetNames($this->getWidgetsThatAreAPI()); + $namesOfWidgetsThatAreAPI = array_map(function ($widget) { + return $widget['uniqueId']; + }, $this->getWidgetsThatAreAPICurrently()); Piwik::postEvent('Platform.initialized'); // userCountryMap defines it's Widgets via this event currently - $currentWidgetNames = array(); - foreach (WidgetsList::get() as $widgets) { - $currentWidgetNames = array_merge($this->getWidgetNames($widgets), $currentWidgetNames); - } + $widgets = API\API::getInstance()->getWidgetMetadata($_GET['idSite']); - $allWidgetNames = array_merge($namesOfWidgetsThatAreAPI, $currentWidgetNames); - $regressedWidgetNames = array_diff($allWidgetNames, $currentWidgetNames); + $currentUniqueIds = array(); + foreach ($widgets as $widget) { + $currentUniqueIds[] = $widget['uniqueId']; + } - $this->assertEmpty($regressedWidgetNames, 'The widgets list is no longer up to date. If you added, removed or renamed a widget please update `getAvailableWidgets()` otherwise you will need to fix it. Different names: ' . var_export($regressedWidgetNames, 1)); - } + $allWidgetNames = array_merge($namesOfWidgetsThatAreAPI, $currentUniqueIds); + $regressedWidgetIds = array_diff($allWidgetNames, $currentUniqueIds); - private function getWidgetNames($widgets) - { - return array_map(function ($widget) { - return $widget['name']; - }, $widgets); + $this->assertEmpty($regressedWidgetIds, 'The widgets list is no longer up to date. If you added or changed a widget please update `getWidgetsThatAreAPICurrently()`, if you removed a widget please add it to `getWidgetsThatAreDeprecatedButStillAPI()`. If the uniqueId changed you might need to create an update for Dashboards and Scheduled Reports! Different names: ' . var_export($regressedWidgetIds, 1)); } /** @@ -78,7 +86,8 @@ class WidgetTest extends SystemTestCase $params = $widget['parameters']; $parameters = array(); - $resolver = new ControllerResolver(StaticContainer::getContainer()); + /** @var ControllerResolver $resolver */ + $resolver = StaticContainer::get('Piwik\Http\ControllerResolver'); $controller = $resolver->getController($params['module'], $params['action'], $parameters); $this->assertNotEmpty($controller, $widget['name'] . ' is not renderable with following params: ' . json_encode($params) . '. This breaks the API, please make sure to keep the URL working'); @@ -86,11 +95,15 @@ class WidgetTest extends SystemTestCase public function availableWidgetsProvider() { - $widgets = $this->getWidgetsThatAreAPI(); - $data = array(); - foreach ($widgets as $widget) { + foreach ($this->getWidgetsThatAreAPICurrently() as $widget) { + if (!empty($widget)) { + $data[] = array($widget); + } + } + + foreach ($this->getWidgetsThatAreDeprecatedButStillAPI() as $widget) { if (!empty($widget)) { $data[] = array($widget); } @@ -99,746 +112,1163 @@ class WidgetTest extends SystemTestCase return $data; } + public function getWidgetsThatAreAPICurrently() + { + return array( + array ( + 'name' => 'Visits Overview (with graph)', + 'uniqueId' => 'widgetVisitOverviewWithGraph', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'VisitOverviewWithGraph', + ), + ),array ( + 'name' => 'Support Piwik!', + 'uniqueId' => 'widgetCoreHomegetDonateForm', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'getDonateForm', + ), + ),array ( + 'name' => 'Welcome!', + 'uniqueId' => 'widgetCoreHomegetPromoVideo', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'getPromoVideo', + ), + ),array ( + 'name' => 'Example Widget Name', + 'uniqueId' => 'widgetExamplePluginmyExampleWidget', + 'parameters' => + array ( + 'module' => 'ExamplePlugin', + 'action' => 'myExampleWidget', + ), + ),array ( + 'name' => 'Top Keywords for Page URL', + 'uniqueId' => 'widgetReferrersgetKeywordsForPage', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getKeywordsForPage', + ), + ),array ( + 'name' => 'Ecommerce Log', + 'uniqueId' => 'widgetEcommercegetEcommerceLog', + 'parameters' => + array ( + 'module' => 'Ecommerce', + 'action' => 'getEcommerceLog', + ), + ),array ( + 'name' => 'SEO Rankings', + 'uniqueId' => 'widgetSEOgetRank', + 'parameters' => + array ( + 'module' => 'SEO', + 'action' => 'getRank', + ), + ),array ( + 'name' => 'Piwik Changelog', + 'uniqueId' => 'widgetExampleRssWidgetrssChangelog', + 'parameters' => + array ( + 'module' => 'ExampleRssWidget', + 'action' => 'rssChangelog', + ), + ),array ( + 'name' => 'Piwik.org Blog', + 'uniqueId' => 'widgetExampleRssWidgetrssPiwik', + 'parameters' => + array ( + 'module' => 'ExampleRssWidget', + 'action' => 'rssPiwik', + ), + ),array ( + 'name' => 'Real-time Map', + 'uniqueId' => 'widgetUserCountryMaprealtimeMap', + 'parameters' => + array ( + 'module' => 'UserCountryMap', + 'action' => 'realtimeMap', + ), + ),array ( + 'name' => 'Visitor Map', + 'uniqueId' => 'widgetUserCountryMapvisitorMap', + 'parameters' => + array ( + 'module' => 'UserCountryMap', + 'action' => 'visitorMap', + ), + ),array ( + 'name' => 'Visitor profile', + 'uniqueId' => 'widgetLivegetVisitorProfilePopup', + 'parameters' => + array ( + 'module' => 'Live', + 'action' => 'getVisitorProfilePopup', + ), + ),array ( + 'name' => 'Visitors in Real-time', + 'uniqueId' => 'widgetLivewidget', + 'parameters' => + array ( + 'module' => 'Live', + 'action' => 'widget', + ), + ),array ( + 'name' => 'Insights Overview', + 'uniqueId' => 'widgetInsightsgetInsightsOverview', + 'parameters' => + array ( + 'module' => 'Insights', + 'action' => 'getInsightsOverview', + ), + ),array ( + 'name' => 'Movers and Shakers', + 'uniqueId' => 'widgetInsightsgetOverallMoversAndShakers', + 'parameters' => + array ( + 'module' => 'Insights', + 'action' => 'getOverallMoversAndShakers', + ), + ),array ( + 'name' => 'Real Time Visitor Count', + 'uniqueId' => 'widgetLivegetSimpleLastVisitCount', + 'parameters' => + array ( + 'module' => 'Live', + 'action' => 'getSimpleLastVisitCount', + ), + ),array ( + 'name' => 'Visits Over Time', + 'uniqueId' => 'widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'graphEvolution', + 'module' => 'VisitsSummary', + 'action' => 'getEvolutionGraph', + ), + ),array ( + 'name' => 'Visits Overview', + 'uniqueId' => 'widgetVisitsSummarygetforceView1viewDataTablesparklines', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'sparklines', + 'module' => 'VisitsSummary', + 'action' => 'get', + ), + ),array ( + 'name' => 'Visitor Log', + 'uniqueId' => 'widgetLivegetLastVisitsDetailsforceView1viewDataTablePiwik%5CPlugins%5CLive%5CVisitorLogsmall1', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'Piwik\\Plugins\\Live\\VisitorLog', + 'module' => 'Live', + 'action' => 'getLastVisitsDetails', + 'small' => 1, + ), + ),array ( + 'name' => 'Custom Variables', + 'uniqueId' => 'widgetCustomVariablesgetCustomVariables', + 'parameters' => + array ( + 'module' => 'CustomVariables', + 'action' => 'getCustomVariables', + ), + ),array ( + 'name' => 'Device type', + 'uniqueId' => 'widgetDevicesDetectiongetType', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getType', + ), + ),array ( + 'name' => 'Device model', + 'uniqueId' => 'widgetDevicesDetectiongetModel', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getModel', + ), + ),array ( + 'name' => 'Device brand', + 'uniqueId' => 'widgetDevicesDetectiongetBrand', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getBrand', + ), + ),array ( + 'name' => 'Screen Resolution', + 'uniqueId' => 'widgetResolutiongetResolution', + 'parameters' => + array ( + 'module' => 'Resolution', + 'action' => 'getResolution', + ), + ),array ( + 'name' => 'Operating System versions', + 'uniqueId' => 'widgetDevicesDetectiongetOsVersions', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getOsVersions', + ), + ),array ( + 'name' => 'Browsers', + 'uniqueId' => 'widgetDevicesDetectiongetBrowsers', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getBrowsers', + ), + ),array ( + 'name' => 'Browser version', + 'uniqueId' => 'widgetDevicesDetectiongetBrowserVersions', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getBrowserVersions', + ), + ),array ( + 'name' => 'Configurations', + 'uniqueId' => 'widgetResolutiongetConfiguration', + 'parameters' => + array ( + 'module' => 'Resolution', + 'action' => 'getConfiguration', + ), + ),array ( + 'name' => 'Operating System families', + 'uniqueId' => 'widgetDevicesDetectiongetOsFamilies', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getOsFamilies', + ), + ),array ( + 'name' => 'Browser engines', + 'uniqueId' => 'widgetDevicesDetectiongetBrowserEnginesviewDataTablegraphPie', + 'parameters' => + array ( + 'viewDataTable' => 'graphPie', + 'module' => 'DevicesDetection', + 'action' => 'getBrowserEngines', + ), + ),array ( + 'name' => 'Browser Plugins', + 'uniqueId' => 'widgetDevicePluginsgetPlugin', + 'parameters' => + array ( + 'module' => 'DevicePlugins', + 'action' => 'getPlugin', + ), + ),array ( + 'name' => 'Country', + 'uniqueId' => 'widgetUserCountrygetCountry', + 'parameters' => + array ( + 'module' => 'UserCountry', + 'action' => 'getCountry', + ), + ),array ( + 'name' => 'Region', + 'uniqueId' => 'widgetUserCountrygetRegion', + 'parameters' => + array ( + 'module' => 'UserCountry', + 'action' => 'getRegion', + ), + ),array ( + 'name' => 'Browser language', + 'uniqueId' => 'widgetUserLanguagegetLanguage', + 'parameters' => + array ( + 'module' => 'UserLanguage', + 'action' => 'getLanguage', + ), + ),array ( + 'name' => 'City', + 'uniqueId' => 'widgetUserCountrygetCity', + 'parameters' => + array ( + 'module' => 'UserCountry', + 'action' => 'getCity', + ), + ),array ( + 'name' => 'Language code', + 'uniqueId' => 'widgetUserLanguagegetLanguageCode', + 'parameters' => + array ( + 'module' => 'UserLanguage', + 'action' => 'getLanguageCode', + ), + ),array ( + 'name' => 'Visits per visit duration', + 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDurationviewDataTablecloud', + 'parameters' => + array ( + 'viewDataTable' => 'cloud', + 'module' => 'VisitorInterest', + 'action' => 'getNumberOfVisitsPerVisitDuration', + ), + ),array ( + 'name' => 'Visits per number of pages', + 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPageviewDataTablecloud', + 'parameters' => + array ( + 'viewDataTable' => 'cloud', + 'module' => 'VisitorInterest', + 'action' => 'getNumberOfVisitsPerPage', + ), + ),array ( + 'name' => 'Visits by Visit Number', + 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByVisitCount', + 'parameters' => + array ( + 'module' => 'VisitorInterest', + 'action' => 'getNumberOfVisitsByVisitCount', + ), + ),array ( + 'name' => 'Visits by Days Since Last Visit', + 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByDaysSinceLast', + 'parameters' => + array ( + 'module' => 'VisitorInterest', + 'action' => 'getNumberOfVisitsByDaysSinceLast', + ), + ),array ( + 'name' => 'Returning Visits Over Time', + 'uniqueId' => 'widgetVisitFrequencygetEvolutionGraphforceView1viewDataTablegraphEvolution', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'graphEvolution', + 'module' => 'VisitFrequency', + 'action' => 'getEvolutionGraph', + ), + ),array ( + 'name' => 'Frequency Overview', + 'uniqueId' => 'widgetVisitFrequencygetforceView1viewDataTablesparklines', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'sparklines', + 'module' => 'VisitFrequency', + 'action' => 'get', + ), + ),array ( + 'name' => 'Visits per local time', + 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTimeviewDataTablegraphVerticalBar', + 'parameters' => + array ( + 'viewDataTable' => 'graphVerticalBar', + 'module' => 'VisitTime', + 'action' => 'getVisitInformationPerLocalTime', + ), + ),array ( + 'name' => 'Visits per server time', + 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTimeviewDataTablegraphVerticalBar', + 'parameters' => + array ( + 'viewDataTable' => 'graphVerticalBar', + 'module' => 'VisitTime', + 'action' => 'getVisitInformationPerServerTime', + ), + ),array ( + 'name' => 'Visits by Day of Week', + 'uniqueId' => 'widgetVisitTimegetByDayOfWeekviewDataTablegraphVerticalBar', + 'parameters' => + array ( + 'viewDataTable' => 'graphVerticalBar', + 'module' => 'VisitTime', + 'action' => 'getByDayOfWeek', + ), + ),array ( + 'name' => 'Pages', + 'uniqueId' => 'widgetActionsgetPageUrls', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getPageUrls', + ), + ),array ( + 'name' => 'Entry pages', + 'uniqueId' => 'widgetActionsgetEntryPageUrls', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getEntryPageUrls', + ), + ),array ( + 'name' => 'Exit pages', + 'uniqueId' => 'widgetActionsgetExitPageUrls', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getExitPageUrls', + ), + ),array ( + 'name' => 'Page titles', + 'uniqueId' => 'widgetActionsgetPageTitles', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getPageTitles', + ), + ),array ( + 'name' => 'Site Search Keywords', + 'uniqueId' => 'widgetActionsgetSiteSearchKeywords', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getSiteSearchKeywords', + ), + ),array ( + 'name' => 'Pages Following a Site Search', + 'uniqueId' => 'widgetActionsgetPageUrlsFollowingSiteSearch', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getPageUrlsFollowingSiteSearch', + ), + ),array ( + 'name' => 'Search Keywords with No Results', + 'uniqueId' => 'widgetActionsgetSiteSearchNoResultKeywords', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getSiteSearchNoResultKeywords', + ), + ),array ( + 'name' => 'Page Titles Following a Site Search', + 'uniqueId' => 'widgetActionsgetPageTitlesFollowingSiteSearch', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getPageTitlesFollowingSiteSearch', + ), + ),array ( + 'name' => 'Search Categories', + 'uniqueId' => 'widgetActionsgetSiteSearchCategories', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getSiteSearchCategories', + ), + ),array ( + 'name' => 'Outlinks', + 'uniqueId' => 'widgetActionsgetOutlinks', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getOutlinks', + ), + ),array ( + 'name' => 'Downloads', + 'uniqueId' => 'widgetActionsgetDownloads', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getDownloads', + ), + ),array ( + 'name' => 'Entry Page Titles', + 'uniqueId' => 'widgetActionsgetEntryPageTitles', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getEntryPageTitles', + ), + ),array ( + 'name' => 'Exit page titles', + 'uniqueId' => 'widgetActionsgetExitPageTitles', + 'parameters' => + array ( + 'module' => 'Actions', + 'action' => 'getExitPageTitles', + ), + ),array ( + 'name' => 'Referrer Types', + 'uniqueId' => 'widgetReferrersgetReferrerTypeviewDataTabletableAllColumns', + 'parameters' => + array ( + 'viewDataTable' => 'tableAllColumns', + 'module' => 'Referrers', + 'action' => 'getReferrerType', + ), + ),array ( + 'name' => 'Referrers', + 'uniqueId' => 'widgetReferrersgetAllviewDataTabletableAllColumns', + 'parameters' => + array ( + 'viewDataTable' => 'tableAllColumns', + 'module' => 'Referrers', + 'action' => 'getAll', + ), + ),array ( + 'name' => 'Keywords', + 'uniqueId' => 'widgetReferrersgetKeywords', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getKeywords', + ), + ),array ( + 'name' => 'Search Engines', + 'uniqueId' => 'widgetReferrersgetSearchEngines', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getSearchEngines', + ), + ),array ( + 'name' => 'Websites', + 'uniqueId' => 'widgetReferrersgetWebsites', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getWebsites', + ), + ),array ( + 'name' => 'Social Networks', + 'uniqueId' => 'widgetReferrersgetSocialsviewDataTablegraphPie', + 'parameters' => + array ( + 'viewDataTable' => 'graphPie', + 'module' => 'Referrers', + 'action' => 'getSocials', + ), + ),array ( + 'name' => 'Campaigns', + 'uniqueId' => 'widgetReferrersgetCampaigns', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getCampaigns', + ), + ),array ( + 'name' => 'Overview', + 'uniqueId' => 'widgetGoalsOverview', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'GoalsOverview', + ), + ),array ( + 'name' => 'Overview', + 'uniqueId' => 'widgetEcommerceOverview', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'EcommerceOverview', + ), + ),array ( + 'name' => 'Download Software', + 'uniqueId' => 'widgetGoal_1', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'Goal_1', + ), + ),array ( + 'name' => 'Download Software2', + 'uniqueId' => 'widgetGoal_2', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'Goal_2', + ), + ),array ( + 'name' => 'Opens Contact Form', + 'uniqueId' => 'widgetGoal_3', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'Goal_3', + ), + ),array ( + 'name' => 'Visit Docs', + 'uniqueId' => 'widgetGoal_4', + 'parameters' => + array ( + 'module' => 'CoreHome', + 'action' => 'renderWidgetContainer', + 'containerId' => 'Goal_4', + ), + ),array ( + 'name' => 'Data tables', + 'uniqueId' => 'widgetExampleUIgetTemperatures', + 'parameters' => + array ( + 'module' => 'ExampleUI', + 'action' => 'getTemperatures', + ), + ),array ( + 'name' => 'Data tables', + 'uniqueId' => 'widgetExampleUIgetTemperaturesforceView1viewDataTablegraphVerticalBar', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'graphVerticalBar', + 'module' => 'ExampleUI', + 'action' => 'getTemperatures', + ), + ),array ( + 'name' => 'Treemap example', + 'uniqueId' => 'widgetExampleUIgetTemperaturesforceView1viewDataTableinfoviz-treemap', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'infoviz-treemap', + 'module' => 'ExampleUI', + 'action' => 'getTemperatures', + ), + ),array ( + 'name' => 'Temperatures evolution over time', + 'uniqueId' => 'widgetExampleUIgetTemperaturesEvolutionforceView1viewDataTablesparklines', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'sparklines', + 'module' => 'ExampleUI', + 'action' => 'getTemperaturesEvolution', + ), + ),array ( + 'name' => 'Evolution of server temperatures over the last few days', + 'uniqueId' => 'widgetExampleUIgetTemperaturesEvolutionforceView1viewDataTablegraphEvolutioncolumnsArray', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'graphEvolution', + 'module' => 'ExampleUI', + 'action' => 'getTemperaturesEvolution', + 'columns' => + array ( + 0 => 'server1', + 1 => 'server2', + ), + ), + ),array ( + 'name' => 'Pie graph', + 'uniqueId' => 'widgetExampleUIgetPlanetRatiosviewDataTablegraphPie', + 'parameters' => + array ( + 'viewDataTable' => 'graphPie', + 'module' => 'ExampleUI', + 'action' => 'getPlanetRatios', + ), + ),array ( + 'name' => 'Simple tag cloud', + 'uniqueId' => 'widgetExampleUIgetPlanetRatiosforceView1viewDataTablecloud', + 'parameters' => + array ( + 'forceView' => 1, + 'viewDataTable' => 'cloud', + 'module' => 'ExampleUI', + 'action' => 'getPlanetRatios', + ), + ),array ( + 'name' => 'Advanced tag cloud: with logos and links', + 'uniqueId' => 'widgetExampleUIgetPlanetRatiosWithLogosviewDataTablecloud', + 'parameters' => + array ( + 'viewDataTable' => 'cloud', + 'module' => 'ExampleUI', + 'action' => 'getPlanetRatiosWithLogos', + ) + ),array ( + 'name' => 'Continent', + 'uniqueId' => 'widgetUserCountrygetContinent', + 'parameters' => + array ( + 'module' => 'UserCountry', + 'action' => 'getContinent', + ), + ), array ( + 'name' => 'Event Categories', + 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getCategory', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Categories', + 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getCategory', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Actions', + 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getAction', + 'secondaryDimension' => 'eventName', + ), + ), array ( + 'name' => 'Event Actions', + 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getAction', + 'secondaryDimension' => 'eventName', + ), + ), array ( + 'name' => 'Event Actions', + 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getAction', + 'secondaryDimension' => 'eventName', + ), + ), array ( + 'name' => 'Event Actions', + 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getAction', + 'secondaryDimension' => 'eventName', + ), + ), array ( + 'name' => 'Event Names', + 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getName', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Names', + 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getName', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Names', + 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getName', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Names', + 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getName', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Categories', + 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getCategory', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Categories', + 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getCategory', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Content Piece', + 'uniqueId' => 'widgetContentsgetContentPieces', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentPieces', + ), + ), array ( + 'name' => 'Content Piece', + 'uniqueId' => 'widgetContentsgetContentPieces', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentPieces', + ), + ), array ( + 'name' => 'Content Name', + 'uniqueId' => 'widgetContentsgetContentNames', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentNames', + ), + ), array ( + 'name' => 'Content Name', + 'uniqueId' => 'widgetContentsgetContentNames', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentNames', + ), + ), array ( + 'name' => 'Content Name', + 'uniqueId' => 'widgetContentsgetContentNames', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentNames', + ), + ), array ( + 'name' => 'Content Name', + 'uniqueId' => 'widgetContentsgetContentNames', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentNames', + ), + ), array ( + 'name' => 'Content Piece', + 'uniqueId' => 'widgetContentsgetContentPieces', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentPieces', + ), + ), array ( + 'name' => 'Content Piece', + 'uniqueId' => 'widgetContentsgetContentPieces', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentPieces', + ), + ), array ( + 'name' => 'Product SKU', + 'uniqueId' => 'widgetGoalsgetItemsSku', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'getItemsSku', + ), + ), array ( + 'name' => 'Product SKU', + 'uniqueId' => 'widgetGoalsgetItemsSku', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'getItemsSku', + ), + ), array ( + 'name' => 'Product Category', + 'uniqueId' => 'widgetGoalsgetItemsCategory', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'getItemsCategory', + ), + ), + ); + } + /** * This is a list of all widgets that we consider API. We need to make sure the widgets will be still renderable * etc. * @return array */ - public function getWidgetsThatAreAPI() + public function getWidgetsThatAreDeprecatedButStillAPI() { - return array ( - array ( - 'name' => 'Visits by Server Time', - 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTime', - 'parameters' => - array ( - 'module' => 'VisitTime', - 'action' => 'getVisitInformationPerServerTime', - ), - ), - array ( - 'name' => 'Visits by Local Time', - 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTime', - 'parameters' => - array ( - 'module' => 'VisitTime', - 'action' => 'getVisitInformationPerLocalTime', - ), - ), - array ( - 'name' => 'Visits by Day of Week', - 'uniqueId' => 'widgetVisitTimegetByDayOfWeek', - 'parameters' => - array ( - 'module' => 'VisitTime', - 'action' => 'getByDayOfWeek', - ), - ), - array ( - 'name' => 'Visits Over Time', - 'uniqueId' => 'widgetVisitsSummarygetEvolutionGraphcolumnsArray', - 'parameters' => - array ( - 'module' => 'VisitsSummary', - 'action' => 'getEvolutionGraph', - 'columns' => - array ( - 0 => 'nb_visits', - ), - ), - ), - array ( - 'name' => 'Visits Overview', - 'uniqueId' => 'widgetVisitsSummarygetSparklines', - 'parameters' => - array ( - 'module' => 'VisitsSummary', - 'action' => 'getSparklines', - ), - ), - array ( - 'name' => 'Visits Overview (with graph)', - 'uniqueId' => 'widgetVisitsSummaryindex', - 'parameters' => - array ( - 'module' => 'VisitsSummary', - 'action' => 'index', - ), - ), - array ( - 'name' => 'Real-time Map', - 'uniqueId' => 'widgetUserCountryMaprealtimeMap', - 'parameters' => - array ( - 'module' => 'UserCountryMap', - 'action' => 'realtimeMap', - ), - ), - array ( - 'name' => 'Visitor Log', - 'uniqueId' => 'widgetLivegetVisitorLogsmall1', - 'parameters' => - array ( - 'module' => 'Live', - 'action' => 'getVisitorLog', - 'small' => 1, - ), - ), - array ( - 'name' => 'Real Time Visitor Count', - 'uniqueId' => 'widgetLivegetSimpleLastVisitCount', - 'parameters' => - array ( - 'module' => 'Live', - 'action' => 'getSimpleLastVisitCount', - ), - ), - array ( - 'name' => 'Visitors in Real-time', - 'uniqueId' => 'widgetLivewidget', - 'parameters' => - array ( - 'module' => 'Live', - 'action' => 'widget', - ), - ), - array ( - 'name' => 'Visitor profile', - 'uniqueId' => 'widgetLivegetVisitorProfilePopup', - 'parameters' => - array ( - 'module' => 'Live', - 'action' => 'getVisitorProfilePopup', - ), - ), - array ( - 'name' => 'Visitor Map', - 'uniqueId' => 'widgetUserCountryMapvisitorMap', - 'parameters' => - array ( - 'module' => 'UserCountryMap', - 'action' => 'visitorMap', - ), - ), - array ( - 'name' => 'Visitor Location (Country)', - 'uniqueId' => 'widgetUserCountrygetCountry', - 'parameters' => - array ( - 'module' => 'UserCountry', - 'action' => 'getCountry', - ), - ), - array ( - 'name' => 'Visitor Location (Continent)', - 'uniqueId' => 'widgetUserCountrygetContinent', - 'parameters' => - array ( - 'module' => 'UserCountry', - 'action' => 'getContinent', - ), - ), - array ( - 'name' => 'Visitor Location (Region)', - 'uniqueId' => 'widgetUserCountrygetRegion', - 'parameters' => - array ( - 'module' => 'UserCountry', - 'action' => 'getRegion', - ), - ), - array ( - 'name' => 'Visitor Location (City)', - 'uniqueId' => 'widgetUserCountrygetCity', - 'parameters' => - array ( - 'module' => 'UserCountry', - 'action' => 'getCity', - ), - ), - array ( - 'name' => 'Custom Variables', - 'uniqueId' => 'widgetCustomVariablesgetCustomVariables', - 'parameters' => - array ( - 'module' => 'CustomVariables', - 'action' => 'getCustomVariables', - ), - ), - array ( - 'name' => 'Length of Visits', - 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDuration', - 'parameters' => - array ( - 'module' => 'VisitorInterest', - 'action' => 'getNumberOfVisitsPerVisitDuration', - ), - ), - array ( - 'name' => 'Pages per Visit', - 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPage', - 'parameters' => - array ( - 'module' => 'VisitorInterest', - 'action' => 'getNumberOfVisitsPerPage', - ), - ), - array ( - 'name' => 'Visits by Visit Number', - 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByVisitCount', - 'parameters' => - array ( - 'module' => 'VisitorInterest', - 'action' => 'getNumberOfVisitsByVisitCount', - ), - ), - array ( - 'name' => 'Visits by Days Since Last Visit', - 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByDaysSinceLast', - 'parameters' => - array ( - 'module' => 'VisitorInterest', - 'action' => 'getNumberOfVisitsByDaysSinceLast', - ), - ), - array ( - 'name' => 'Frequency Overview', - 'uniqueId' => 'widgetVisitFrequencygetSparklines', - 'parameters' => - array ( - 'module' => 'VisitFrequency', - 'action' => 'getSparklines', - ), - ), - array ( - 'name' => 'Returning Visits Over Time', - 'uniqueId' => 'widgetVisitFrequencygetEvolutionGraphcolumnsArray', - 'parameters' => - array ( - 'module' => 'VisitFrequency', - 'action' => 'getEvolutionGraph', - 'columns' => - array ( - 0 => 'nb_visits_returning', - ), - ), - ), - array ( - 'name' => 'Screen Resolution', - 'uniqueId' => 'widgetResolutiongetResolution', - 'parameters' => - array ( - 'module' => 'Resolution', - 'action' => 'getResolution', - ), - ), - array ( - 'name' => 'Browser Plugins', - 'uniqueId' => 'widgetDevicePluginsgetPlugin', - 'parameters' => - array ( - 'module' => 'DevicePlugins', - 'action' => 'getPlugin', - ), - ), - array ( - 'name' => 'Visitor Configuration', - 'uniqueId' => 'widgetResolutiongetConfiguration', - 'parameters' => - array ( - 'module' => 'Resolution', - 'action' => 'getConfiguration', - ), - ), - array ( - 'name' => 'Browser language', - 'uniqueId' => 'widgetUserLanguagegetLanguage', - 'parameters' => - array ( - 'module' => 'UserLanguage', - 'action' => 'getLanguage', - ), - ), - array ( - 'name' => 'Language code', - 'uniqueId' => 'widgetUserLanguagegetLanguageCode', - 'parameters' => - array ( - 'module' => 'UserLanguage', - 'action' => 'getLanguageCode', - ), - ), - array ( - 'name' => 'Device type', - 'uniqueId' => 'widgetDevicesDetectiongetType', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getType', - ), - ), - array ( - 'name' => 'Device brand', - 'uniqueId' => 'widgetDevicesDetectiongetBrand', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getBrand', - ), - ), - array ( - 'name' => 'Visitor Browser', - 'uniqueId' => 'widgetDevicesDetectiongetBrowsers', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getBrowsers', - ), - ), - array ( - 'name' => 'Device model', - 'uniqueId' => 'widgetDevicesDetectiongetModel', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getModel', - ), - ), - array ( - 'name' => 'Browser version', - 'uniqueId' => 'widgetDevicesDetectiongetBrowserVersions', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getBrowserVersions', - ), - ), - array ( - 'name' => 'Operating System families', - 'uniqueId' => 'widgetDevicesDetectiongetOsFamilies', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getOsFamilies', - ), - ), - array ( - 'name' => 'Operating System versions', - 'uniqueId' => 'widgetDevicesDetectiongetOsVersions', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getOsVersions', - ), - ), - array ( - 'name' => 'Browser engines', - 'uniqueId' => 'widgetDevicesDetectiongetBrowserEngines', - 'parameters' => - array ( - 'module' => 'DevicesDetection', - 'action' => 'getBrowserEngines', - ), - ), - array ( - 'name' => 'Pages', - 'uniqueId' => 'widgetActionsgetPageUrls', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getPageUrls', - ), - ), - array ( - 'name' => 'Entry Pages', - 'uniqueId' => 'widgetActionsgetEntryPageUrls', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getEntryPageUrls', - ), - ), - array ( - 'name' => 'Exit Pages', - 'uniqueId' => 'widgetActionsgetExitPageUrls', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getExitPageUrls', - ), - ), - array ( - 'name' => 'Page Titles', - 'uniqueId' => 'widgetActionsgetPageTitles', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getPageTitles', - ), - ), - array ( - 'name' => 'Entry Page Titles', - 'uniqueId' => 'widgetActionsgetEntryPageTitles', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getEntryPageTitles', - ), - ), - array ( - 'name' => 'Exit Page Titles', - 'uniqueId' => 'widgetActionsgetExitPageTitles', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getExitPageTitles', - ), - ), - array ( - 'name' => 'Outlinks', - 'uniqueId' => 'widgetActionsgetOutlinks', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getOutlinks', - ), - ), - array ( - 'name' => 'Downloads', - 'uniqueId' => 'widgetActionsgetDownloads', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getDownloads', - ), - ), - array ( - 'name' => 'Content Name', - 'uniqueId' => 'widgetContentsgetContentNames', - 'parameters' => - array ( - 'module' => 'Contents', - 'action' => 'getContentNames', - ), - ), - array ( - 'name' => 'Content Piece', - 'uniqueId' => 'widgetContentsgetContentPieces', - 'parameters' => - array ( - 'module' => 'Contents', - 'action' => 'getContentPieces', - ), - ), - array ( - 'name' => 'Event Categories', - 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction', - 'parameters' => - array ( - 'module' => 'Events', - 'action' => 'getCategory', - 'secondaryDimension' => 'eventAction', - ), - ), - array ( - 'name' => 'Event Actions', - 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName', - 'parameters' => - array ( - 'module' => 'Events', - 'action' => 'getAction', - 'secondaryDimension' => 'eventName', - ), - ), - array ( - 'name' => 'Event Names', - 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction', - 'parameters' => - array ( - 'module' => 'Events', - 'action' => 'getName', - 'secondaryDimension' => 'eventAction', - ), - ), - array ( - 'name' => 'Site Search Keywords', - 'uniqueId' => 'widgetActionsgetSiteSearchKeywords', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getSiteSearchKeywords', - ), - ), - array ( - 'name' => 'Search Keywords with No Results', - 'uniqueId' => 'widgetActionsgetSiteSearchNoResultKeywords', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getSiteSearchNoResultKeywords', - ), - ), - array ( - 'name' => 'Search Categories', - 'uniqueId' => 'widgetActionsgetSiteSearchCategories', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getSiteSearchCategories', - ), - ), - array ( - 'name' => 'Pages Following a Site Search', - 'uniqueId' => 'widgetActionsgetPageUrlsFollowingSiteSearch', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getPageUrlsFollowingSiteSearch', - ), - ), - array ( - 'name' => 'Page Titles Following a Site Search', - 'uniqueId' => 'widgetActionsgetPageTitlesFollowingSiteSearch', - 'parameters' => - array ( - 'module' => 'Actions', - 'action' => 'getPageTitlesFollowingSiteSearch', - ), - ), - array ( - 'name' => 'Overview', - 'uniqueId' => 'widgetReferrersgetReferrerType', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getReferrerType', - ), - ), - array ( - 'name' => 'All Referrers', - 'uniqueId' => 'widgetReferrersgetAll', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getAll', - ), - ), - array ( - 'name' => 'Keywords', - 'uniqueId' => 'widgetReferrersgetKeywords', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getKeywords', - ), - ), - array ( - 'name' => 'Referrer Websites', - 'uniqueId' => 'widgetReferrersgetWebsites', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getWebsites', - ), - ), - array ( - 'name' => 'Search Engines', - 'uniqueId' => 'widgetReferrersgetSearchEngines', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getSearchEngines', - ), - ), - array ( - 'name' => 'Campaigns', - 'uniqueId' => 'widgetReferrersgetCampaigns', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getCampaigns', - ), - ), - array ( - 'name' => 'List of social networks', - 'uniqueId' => 'widgetReferrersgetSocials', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getSocials', - ), - ), - array ( - 'name' => 'Goals Overview', - 'uniqueId' => 'widgetGoalswidgetGoalsOverview', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'widgetGoalsOverview', - ), - ), - array ( - 'name' => 'Download Software', - 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal1', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'widgetGoalReport', - 'idGoal' => '1', - ), - ), - array ( - 'name' => 'Download Software2', - 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal2', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'widgetGoalReport', - 'idGoal' => '2', - ), - ), - array ( - 'name' => 'Opens Contact Form', - 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal3', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'widgetGoalReport', - 'idGoal' => '3', - ), - ), - array ( - 'name' => 'Visit Docs', - 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal4', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'widgetGoalReport', - 'idGoal' => '4', - ), - ), - array ( - 'name' => 'Product SKU', - 'uniqueId' => 'widgetGoalsgetItemsSku', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'getItemsSku', - ), - ), - array ( - 'name' => 'Product Name', - 'uniqueId' => 'widgetGoalsgetItemsName', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'getItemsName', - ), - ), - array ( - 'name' => 'Product Category', - 'uniqueId' => 'widgetGoalsgetItemsCategory', - 'parameters' => - array ( - 'module' => 'Goals', - 'action' => 'getItemsCategory', - ), - ), - array ( - 'name' => 'Overview', - 'uniqueId' => 'widgetEcommercewidgetGoalReportidGoalecommerceOrder', - 'parameters' => - array ( - 'module' => 'Ecommerce', - 'action' => 'widgetGoalReport', - 'idGoal' => 'ecommerceOrder', - ), - ), - array ( - 'name' => 'Ecommerce Log', - 'uniqueId' => 'widgetEcommercegetEcommerceLog', - 'parameters' => - array ( - 'module' => 'Ecommerce', - 'action' => 'getEcommerceLog', - ), - ), - array ( - 'name' => 'Insights Overview', - 'uniqueId' => 'widgetInsightsgetInsightsOverview', - 'parameters' => - array ( - 'module' => 'Insights', - 'action' => 'getInsightsOverview', - ), - ), - array ( - 'name' => 'Movers and Shakers', - 'uniqueId' => 'widgetInsightsgetOverallMoversAndShakers', - 'parameters' => - array ( - 'module' => 'Insights', - 'action' => 'getOverallMoversAndShakers', - ), - ), - array ( - 'name' => 'Top Keywords for Page URL', - 'uniqueId' => 'widgetReferrersgetKeywordsForPage', - 'parameters' => - array ( - 'module' => 'Referrers', - 'action' => 'getKeywordsForPage', - ), - ), - array ( - 'name' => 'SEO Rankings', - 'uniqueId' => 'widgetSEOgetRank', - 'parameters' => - array ( - 'module' => 'SEO', - 'action' => 'getRank', - ), - ), - array ( - 'name' => 'Support Piwik!', - 'uniqueId' => 'widgetCoreHomegetDonateForm', - 'parameters' => - array ( - 'module' => 'CoreHome', - 'action' => 'getDonateForm', - ), - ), - array ( - 'name' => 'Welcome!', - 'uniqueId' => 'widgetCoreHomegetPromoVideo', - 'parameters' => - array ( - 'module' => 'CoreHome', - 'action' => 'getPromoVideo', - ), - ), - array ( - 'name' => 'Piwik.org Blog', - 'uniqueId' => 'widgetExampleRssWidgetrssPiwik', - 'parameters' => - array ( - 'module' => 'ExampleRssWidget', - 'action' => 'rssPiwik', - ), - ), - array ( - 'name' => 'Piwik Changelog', - 'uniqueId' => 'widgetExampleRssWidgetrssChangelog', - 'parameters' => - array ( - 'module' => 'ExampleRssWidget', - 'action' => 'rssChangelog', - ), - ), + return array( + array ( + 'name' => 'Visits per server time', + 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTime', + 'parameters' => + array ( + 'module' => 'VisitTime', + 'action' => 'getVisitInformationPerServerTime', + ), + ), array ( + 'name' => 'Visits per local time', + 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTime', + 'parameters' => + array ( + 'module' => 'VisitTime', + 'action' => 'getVisitInformationPerLocalTime', + ), + ), array ( + 'name' => 'Visits by Day of Week', + 'uniqueId' => 'widgetVisitTimegetByDayOfWeek', + 'parameters' => + array ( + 'module' => 'VisitTime', + 'action' => 'getByDayOfWeek', + ), + ), array ( + 'name' => 'Visits Over Time', + 'uniqueId' => 'widgetVisitsSummarygetEvolutionGraphcolumnsArray', + 'parameters' => + array ( + 'module' => 'VisitsSummary', + 'action' => 'getEvolutionGraph', + 'columns' => + array ( + 0 => 'nb_visits', + ), + ), + ), array ( + 'name' => 'Visits Overview', + 'uniqueId' => 'widgetVisitsSummarygetSparklines', + 'parameters' => + array ( + 'module' => 'VisitsSummary', + 'action' => 'getSparklines', + ), + ), array ( + 'name' => 'Visits Overview (with graph)', + 'uniqueId' => 'widgetVisitsSummaryindex', + 'parameters' => + array ( + 'module' => 'VisitsSummary', + 'action' => 'index', + ), + ), array ( + 'name' => 'Visitor Log', + 'uniqueId' => 'widgetLivegetVisitorLogsmall1', + 'parameters' => + array ( + 'module' => 'Live', + 'action' => 'getVisitorLog', + 'small' => 1, + ), + ), array ( + 'name' => 'Continent', + 'uniqueId' => 'widgetUserCountrygetContinent', + 'parameters' => + array ( + 'module' => 'UserCountry', + 'action' => 'getContinent', + ), + ), array ( + 'name' => 'Visits per visit duration', + 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDuration', + 'parameters' => + array ( + 'module' => 'VisitorInterest', + 'action' => 'getNumberOfVisitsPerVisitDuration', + ), + ), array ( + 'name' => 'Pages per Visit', + 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPage', + 'parameters' => + array ( + 'module' => 'VisitorInterest', + 'action' => 'getNumberOfVisitsPerPage', + ), + ), array ( + 'name' => 'Frequency Overview', + 'uniqueId' => 'widgetVisitFrequencygetSparklines', + 'parameters' => + array ( + 'module' => 'VisitFrequency', + 'action' => 'getSparklines', + ), + ), array ( + 'name' => 'Returning Visits Over Time', + 'uniqueId' => 'widgetVisitFrequencygetEvolutionGraphcolumnsArray', + 'parameters' => + array ( + 'module' => 'VisitFrequency', + 'action' => 'getEvolutionGraph', + 'columns' => + array ( + 0 => 'nb_visits_returning', + ), + ), + ), array ( + 'name' => 'Browser engines', + 'uniqueId' => 'widgetDevicesDetectiongetBrowserEngines', + 'parameters' => + array ( + 'module' => 'DevicesDetection', + 'action' => 'getBrowserEngines', + ), + ), array ( + 'name' => 'Content Name', + 'uniqueId' => 'widgetContentsgetContentNames', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentNames', + ), + ), array ( + 'name' => 'Content Piece', + 'uniqueId' => 'widgetContentsgetContentPieces', + 'parameters' => + array ( + 'module' => 'Contents', + 'action' => 'getContentPieces', + ), + ), array ( + 'name' => 'Event Categories', + 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getCategory', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Event Actions', + 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getAction', + 'secondaryDimension' => 'eventName', + ), + ), array ( + 'name' => 'Event Names', + 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction', + 'parameters' => + array ( + 'module' => 'Events', + 'action' => 'getName', + 'secondaryDimension' => 'eventAction', + ), + ), array ( + 'name' => 'Overview', + 'uniqueId' => 'widgetReferrersgetReferrerType', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getReferrerType', + ), + ), array ( + 'name' => 'All Referrers', + 'uniqueId' => 'widgetReferrersgetAll', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getAll', + ), + ), array ( + 'name' => 'List of social networks', + 'uniqueId' => 'widgetReferrersgetSocials', + 'parameters' => + array ( + 'module' => 'Referrers', + 'action' => 'getSocials', + ), + ), array ( + 'name' => 'Goals Overview', + 'uniqueId' => 'widgetGoalswidgetGoalsOverview', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'widgetGoalsOverview', + ), + ), array ( + 'name' => 'Download Software', + 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal1', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'widgetGoalReport', + 'idGoal' => '1', + ), + ), array ( + 'name' => 'Download Software2', + 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal2', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'widgetGoalReport', + 'idGoal' => '2', + ), + ), array ( + 'name' => 'Opens Contact Form', + 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal3', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'widgetGoalReport', + 'idGoal' => '3', + ), + ), array ( + 'name' => 'Visit Docs', + 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal4', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'widgetGoalReport', + 'idGoal' => '4', + ), + ), array ( + 'name' => 'Product SKU', + 'uniqueId' => 'widgetGoalsgetItemsSku', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'getItemsSku', + ), + ), array ( + 'name' => 'Product Name', + 'uniqueId' => 'widgetGoalsgetItemsName', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'getItemsName', + ), + ), array ( + 'name' => 'Product Category', + 'uniqueId' => 'widgetGoalsgetItemsCategory', + 'parameters' => + array ( + 'module' => 'Goals', + 'action' => 'getItemsCategory', + ), + ), array ( + 'name' => 'Overview', + 'uniqueId' => 'widgetEcommercewidgetGoalReportidGoalecommerceOrder', + 'parameters' => + array ( + 'module' => 'Ecommerce', + 'action' => 'widgetGoalReport', + 'idGoal' => 'ecommerceOrder', + ), + ) ); } } -WidgetTest::$fixture = new WidgetizeFixture();
\ No newline at end of file +WidgetTest::$fixture = new WidgetizeFixture(); diff --git a/plugins/ZenMode/ZenMode.php b/plugins/ZenMode/ZenMode.php index bbacb157c3..eee60c4603 100644 --- a/plugins/ZenMode/ZenMode.php +++ b/plugins/ZenMode/ZenMode.php @@ -40,6 +40,7 @@ class ZenMode extends \Piwik\Plugin { $jsFiles[] = "plugins/ZenMode/javascripts/zen-mode.js"; $jsFiles[] = "plugins/ZenMode/angularjs/quick-access/quick-access.directive.js"; + $jsFiles[] = "plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js"; $jsFiles[] = "plugins/ZenMode/angularjs/zen-mode/zen-mode-switcher.directive.js"; } diff --git a/plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js b/plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js new file mode 100644 index 0000000000..420020b75f --- /dev/null +++ b/plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js @@ -0,0 +1,35 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * Usage: + * <div piwik-zen-mode-switcher>...</div> + * Will toggle the zen mode on click on this element. + */ +(function () { + angular.module('piwikApp').directive('piwikReportingMenu', piwikZenModeSwitcher); + + piwikZenModeSwitcher.$inject = ['$rootElement', '$filter']; + + function piwikZenModeSwitcher($rootElement, $filter) { + + return { + restrict: 'A', + compile: function (element, attrs) { + + element.find('.Menu--dashboard').prepend( + '<span piwik-zen-mode-switcher class="deactivateZenMode">' + + '<img src="plugins/CoreHome/images/navigation_collapse.png" >' + + '</span>'); + + return function () { + }; + } + }; + + } +})();
\ No newline at end of file diff --git a/plugins/ZenMode/javascripts/zen-mode.js b/plugins/ZenMode/javascripts/zen-mode.js index bc2a793942..e4e163db8b 100644 --- a/plugins/ZenMode/javascripts/zen-mode.js +++ b/plugins/ZenMode/javascripts/zen-mode.js @@ -19,13 +19,6 @@ $(document).ready(function () { piwikHelper.compileAngularComponents(addedElement); - addedElement = $('.Menu--dashboard').prepend( - '<span piwik-zen-mode-switcher class="deactivateZenMode">' - + '<img src="plugins/CoreHome/images/navigation_collapse.png" >' - + '</span>'); - - piwikHelper.compileAngularComponents(addedElement); - angular.element(document).injector().invoke(handleZenMode); function handleZenMode ($rootElement, $cookies) { @@ -77,7 +70,7 @@ $(document).ready(function () { function isDashboard() { - return !!$('.Menu--dashboard').length; + return !!$('[piwik-reporting-menu]').length; } function initMenu () { @@ -98,7 +91,6 @@ $(document).ready(function () { $('#Searchmenu').off('keydown focus', '.quick-access input', showQuickAccessMenu); $('#Searchmenu').off('blur', '.quick-access input', hideQuickAccessMenu); - menu.prototype.adaptSubMenuHeight(); } function overMainLI () { |