diff options
author | Thomas Steur <thomas.steur@gmail.com> | 2015-12-10 00:50:07 +0300 |
---|---|---|
committer | Thomas Steur <thomas.steur@gmail.com> | 2015-12-10 00:50:07 +0300 |
commit | ebced800c0184f5ce93663afab6b0337c3755cbb (patch) | |
tree | fb26c2506d37586ff0d38fb902d63d17701bd883 /plugins | |
parent | 3d26af780d8fe0e4f7c5824a88b95c4a0d0d25d7 (diff) | |
parent | db36ca384809ea36457a465a3b6d1ecf951b0e26 (diff) |
Merge master into "3.0"
Conflicts:
.travis.yml
CHANGELOG.md
core/Plugin/ControllerAdmin.php
core/Version.php
core/testMinimumPhpVersion.php
plugins/API/API.php
plugins/Actions/Reports/GetPageUrls.php
plugins/AnonymousPiwikUsageMeasurement
plugins/CoreHome/CoreHome.php
plugins/CoreHome/templates/_menu.twig
plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml
plugins/Goals/Controller.php
tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
tests/PHPUnit/Integration/Plugin/ManagerTest.php
tests/PHPUnit/System/expected/test_FlattenReports__CustomVariables.getCustomVariables_day.xml
tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html
tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html
tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI__CustomVariables.getCustomVariables_range.xml
tests/PHPUnit/System/expected/test_periodIsRange_dateIsLastN_MetadataAndNormalAPI_pagesegment__CustomVariables.getCustomVariables_range.xml
tests/PHPUnit/System/expected/test_reportLimiting__CustomVariables.getCustomVariables_day.xml
tests/PHPUnit/System/expected/test_reportLimiting_rankingQuery__CustomVariables.getCustomVariables_day.xml
tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_day.xml
tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchALL_noGoalData__CustomVariables.getCustomVariables_week.xml
tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_day.xml
tests/PHPUnit/System/expected/test_twoVisitsWithCustomVariables_segmentMatchVisitorType__CustomVariables.getCustomVariables_week.xml
tests/UI/expected-ui-screenshots
tests/UI/specs/Overlay_spec.js
tests/UI/specs/UIIntegration_spec.js
Diffstat (limited to 'plugins')
380 files changed, 5476 insertions, 1393 deletions
diff --git a/plugins/API/API.php b/plugins/API/API.php index 8dcaf0186b..4eab2c63fd 100644 --- a/plugins/API/API.php +++ b/plugins/API/API.php @@ -91,8 +91,9 @@ class API extends \Piwik\Plugin\API * are not visible in the UI and not present in the API meta data. These columns are * translated here. * @return array + * @deprecated since Piwik 2.15.1 */ - public static function getDefaultMetricTranslations() + public function getDefaultMetricTranslations() { return Metrics::getDefaultMetricTranslations(); } @@ -105,6 +106,8 @@ class API extends \Piwik\Plugin\API */ public function getAvailableMeasurableTypes() { + Piwik::checkUserHasSomeViewAccess(); + $typeManager = new TypeManager(); $types = $typeManager->getAllTypes(); @@ -123,11 +126,17 @@ class API extends \Piwik\Plugin\API public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true) { - $isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous(); + if (empty($idSites)) { + Piwik::checkUserHasSomeViewAccess(); + } else { + Piwik::checkUserHasViewAccess($idSites); + } + + $isNotAnonymous = !Piwik::isUserIsAnonymous(); $sites = (is_array($idSites) ? implode('.', $idSites) : (int) $idSites); $cache = Cache::getTransientCache(); - $cachKey = 'API.getSegmentsMetadata' . $sites . '_' . (int) $_hideImplementationData . '_' . (int) $isAuthenticatedWithViewAccess; + $cachKey = 'API.getSegmentsMetadata' . $sites . '_' . (int) $_hideImplementationData . '_' . (int) $isNotAnonymous; $cachKey = CacheId::pluginAware($cachKey); if ($cache->contains($cachKey)) { @@ -135,7 +144,7 @@ class API extends \Piwik\Plugin\API } $metadata = new SegmentMetadata(); - $segments = $metadata->getSegmentsMetadata($idSites, $_hideImplementationData, $isAuthenticatedWithViewAccess); + $segments = $metadata->getSegmentsMetadata($idSites, $_hideImplementationData, $isNotAnonymous); $cache->save($cachKey, $segments); @@ -164,6 +173,7 @@ class API extends \Piwik\Plugin\API * * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL. * @return string + * @deprecated since Piwik 2.15.1 */ public function getLogoUrl($pathOnly = false) { @@ -176,6 +186,7 @@ class API extends \Piwik\Plugin\API * * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL. * @return string + * @deprecated since Piwik 2.15.1 */ public function getHeaderLogoUrl($pathOnly = false) { @@ -214,6 +225,8 @@ class API extends \Piwik\Plugin\API public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false, $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false) { + Piwik::checkUserHasViewAccess($idSite); + if ($language) { /** @var Translator $translator */ $translator = StaticContainer::get('Piwik\Translation\Translator'); @@ -239,6 +252,8 @@ class API extends \Piwik\Plugin\API public function getReportMetadata($idSites = '', $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false) { + Piwik::checkUserHasViewAccess($idSites); + $reporter = new ProcessedReport(); $metadata = $reporter->getReportMetadata($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports); return $metadata; @@ -247,11 +262,13 @@ class API extends \Piwik\Plugin\API public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, $apiParameters = false, $idGoal = false, $language = false, $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false, - $format_metrics = null) + $format_metrics = null, $idDimension = false) { + Piwik::checkUserHasViewAccess($idSite); + $reporter = new ProcessedReport(); $processed = $reporter->getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment, - $apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics); + $apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics, $idDimension); return $processed; } @@ -296,6 +313,8 @@ class API extends \Piwik\Plugin\API */ public function get($idSite, $period, $date, $segment = false, $columns = false) { + Piwik::checkUserHasViewAccess($idSite); + $columns = Piwik::getArrayFromApiParameter($columns); // build columns map for faster checks later on @@ -376,13 +395,16 @@ class API extends \Piwik\Plugin\API * @param bool|int $idGoal * @param bool|string $legendAppendMetric * @param bool|string $labelUseAbsoluteUrl + * @param bool|int $idDimension * @return array */ - public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true) + public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false) { + Piwik::checkUserHasViewAccess($idSite); + $rowEvolution = new RowEvolution(); return $rowEvolution->getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $column, - $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl); + $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl, $idDimension); } /** @@ -442,30 +464,20 @@ class API extends \Piwik\Plugin\API if (empty(Config::getInstance()->General['enable_segment_suggested_values'])) { return array(); } + Piwik::checkUserHasViewAccess($idSite); $maxSuggestionsToReturn = 30; - $segmentsMetadata = $this->getSegmentsMetadata($idSite, $_hideImplementationData = false); - - $segmentFound = false; - foreach ($segmentsMetadata as $segmentMetadata) { - if ($segmentMetadata['segment'] == $segmentName) { - $segmentFound = $segmentMetadata; - break; - } - } - if (empty($segmentFound)) { - throw new \Exception("Requested segment not found."); - } + $segment = $this->findSegment($segmentName, $idSite); // if segment has suggested values callback then return result from it instead $suggestedValuesCallbackRequiresTable = false; - if (isset($segmentFound['suggestedValuesCallback'])) { + if (isset($segment['suggestedValuesCallback'])) { $suggestedValuesCallbackRequiresTable = $this->doesSuggestedValuesCallbackNeedData( - $segmentFound['suggestedValuesCallback']); + $segment['suggestedValuesCallback']); if (!$suggestedValuesCallbackRequiresTable) { - return call_user_func($segmentFound['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn); + return call_user_func($segment['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn); } } @@ -474,6 +486,57 @@ class API extends \Piwik\Plugin\API return array(); } + if (!empty($segment['unionOfSegments'])) { + $values = array(); + foreach ($segment['unionOfSegments'] as $unionSegmentName) { + $unionSegment = $this->findSegment($unionSegmentName, $idSite); + + try { + $result = $this->getSuggestedValuesForSegmentName($idSite, $unionSegment, $maxSuggestionsToReturn); + if (!empty($result)) { + $values = array_merge($result, $values); + } + } catch (\Exception $e) { + // we ignore if there was no data found for $unionSegmentName + } + } + + if (empty($values)) { + throw new \Exception("There was no data to suggest for $segmentName"); + } + + } else { + $values = $this->getSuggestedValuesForSegmentName($idSite, $segment, $maxSuggestionsToReturn); + } + + $values = $this->getMostFrequentValues($values); + $values = array_slice($values, 0, $maxSuggestionsToReturn); + $values = array_map(array('Piwik\Common', 'unsanitizeInputValue'), $values); + + return $values; + } + + private function findSegment($segmentName, $idSite) + { + $segmentsMetadata = $this->getSegmentsMetadata($idSite, $_hideImplementationData = false); + + $segmentFound = false; + foreach ($segmentsMetadata as $segmentMetadata) { + if ($segmentMetadata['segment'] == $segmentName) { + $segmentFound = $segmentMetadata; + break; + } + } + + if (empty($segmentFound)) { + throw new \Exception("Requested segment $segmentName not found."); + } + + return $segmentFound; + } + + private function getSuggestedValuesForSegmentName($idSite, $segment, $maxSuggestionsToReturn) + { $startDate = Date::now()->subDay(60)->toString(); $requestLastVisits = "method=Live.getLastVisitsDetails &idSite=$idSite @@ -483,6 +546,8 @@ class API extends \Piwik\Plugin\API &serialize=0 &flat=1"; + $segmentName = $segment['segment']; + // Select non empty fields only // Note: this optimization has only a very minor impact $requestLastVisits .= "&segment=$segmentName" . urlencode('!='); @@ -497,22 +562,18 @@ class API extends \Piwik\Plugin\API $request = new Request($requestLastVisits); $table = $request->process(); + if (empty($table)) { throw new \Exception("There was no data to suggest for $segmentName"); } - if ($suggestedValuesCallbackRequiresTable) { - $values = call_user_func($segmentFound['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table); + if (isset($segment['suggestedValuesCallback']) && + $this->doesSuggestedValuesCallbackNeedData($segment['suggestedValuesCallback'])) { + $values = call_user_func($segment['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table); } else { $values = $this->getSegmentValuesFromVisitorLog($segmentName, $table); } - $values = $this->getMostFrequentValues($values); - - $values = array_slice($values, 0, $maxSuggestionsToReturn); - - $values = array_map(array('Piwik\Common', 'unsanitizeInputValue'), $values); - return $values; } diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php index 65b59a2051..8a9a96b537 100644 --- a/plugins/API/ProcessedReport.php +++ b/plugins/API/ProcessedReport.php @@ -298,17 +298,25 @@ class ProcessedReport public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, $apiParameters = false, $idGoal = false, $language = false, $showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false, - $formatMetrics = null) + $formatMetrics = null, $idDimension = false) { $timer = new Timer(); if (empty($apiParameters)) { $apiParameters = array(); } + if (!empty($idGoal) && empty($apiParameters['idGoal']) ) { $apiParameters['idGoal'] = $idGoal; } + + if (!empty($idDimension) + && empty($apiParameters['idDimension']) + ) { + $apiParameters['idDimension'] = (int) $idDimension; + } + // Is this report found in the Metadata available reports? $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc, $showSubtableReports = true); diff --git a/plugins/API/RowEvolution.php b/plugins/API/RowEvolution.php index c708608089..711a48316c 100644 --- a/plugins/API/RowEvolution.php +++ b/plugins/API/RowEvolution.php @@ -37,7 +37,7 @@ class RowEvolution 'getPageUrl' ); - public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true) + public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false) { // validation of requested $period & $date if ($period == 'range') { @@ -52,9 +52,9 @@ class RowEvolution $label = DataTablePostProcessor::unsanitizeLabelParameter($label); $labels = Piwik::getArrayFromApiParameter($label); - $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal); + $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal, $idDimension); - $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal); + $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal, $idDimension); if (empty($labels)) { $labels = $this->getLabelsFromDataTable($dataTable, $labels); @@ -249,7 +249,7 @@ class RowEvolution * @throws Exception * @return DataTable\Map|DataTable */ - private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false) + private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false, $idDimension = false) { if (!is_array($label)) { $label = array($label); @@ -266,6 +266,7 @@ class RowEvolution 'serialize' => '0', 'segment' => $segment, 'idGoal' => $idGoal, + 'idDimension' => $idDimension, // data for row evolution should NOT be limited 'filter_limit' => -1, @@ -310,12 +311,15 @@ class RowEvolution * @throws Exception * @return array */ - private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false) + private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false, $idDimension = false) { $apiParameters = array(); if (!empty($idGoal) && $idGoal > 0) { $apiParameters = array('idGoal' => $idGoal); } + if (!empty($idDimension) && $idDimension > 0) { + $apiParameters = array('idDimension' => (int) $idDimension); + } $reportMetadata = API::getInstance()->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc = false, $showSubtableReports = true); diff --git a/plugins/API/lang/cs.json b/plugins/API/lang/cs.json index 68c93a8e58..32d7dcba79 100644 --- a/plugins/API/lang/cs.json +++ b/plugins/API/lang/cs.json @@ -4,9 +4,9 @@ "KeepTokenSecret": "Tento token_auth je tajný jako vaše uživatelské jméno a heslo, %s neříkejte jej nikomu jinému %s!", "LoadedAPIs": "Úspěšně načteno %s API", "MoreInformation": "Pro více informací o API Piwiku se podívejte na %s Úvod do API Piwiku %s a %s Referenci API Piwiku %s", - "PluginDescription": "Všechna data v Piwiku jsou dostupná pomocí jednoduchých API. Tento zásuvný modul je vstupním bodem těchto webových služeb, který vám umožňuje získat vaše analitická data jako XML, JSON, CSV, PHP atd.", + "PluginDescription": "Všechna data v Piwiku jsou dostupná pomocí jednoduchých API. Tento zásuvný modul je vstupním bodem těchto webových služeb, který vám umožňuje získat vaše analytická data jako XML, JSON, CSV, PHP atd.", "ReportingApiReference": "Reference API hlášení", - "TopLinkTooltip": "Zpřístupněte Vaše Webové analýzy programově skrze jednoduché API pomocí json, xml a dalších.", + "TopLinkTooltip": "Zpřístupněte svoje Webové analýzy programově skrze jednoduché API pomocí json, xml a dalších.", "UserAuthentication": "Autentifikace uživatele", "UsingTokenAuth": "Pokud chcete %s načíst data ze skriptu, cronu, atd. %s Potřebujete přidat parametr %s k voláním API, které vyžadují přihlášení", "Glossary": "Glosář", diff --git a/plugins/API/lang/ja.json b/plugins/API/lang/ja.json index 7e65064104..f32bf15992 100644 --- a/plugins/API/lang/ja.json +++ b/plugins/API/lang/ja.json @@ -5,8 +5,11 @@ "LoadedAPIs": "%s API が正常に読み込まれました", "MoreInformation": "Piwik API の詳細については、%sIntroduction to Piwik API%s や %sPiwik API Reference%s を参照してください。", "PluginDescription": "Pwik の全てのデータは簡単な API を通して利用することができます。このプラグインはウェブサービスの入り口で、xml , json , php , csv などのあなたのウェブ分析データを入手するために呼び出すことができます。", + "ReportingApiReference": "API リファレンスを報告", "TopLinkTooltip": "jsopn、xml等シンプルなAPIを介して、プログラムで分析データにアクセスできます", "UserAuthentication": "ユーザー認証", - "UsingTokenAuth": "%sスクリプト(crontab 等)でリクエストデータを得たい場合%sは、API をコールする URL(認証が必要)にパラメータ %s を付加する必要があります。" + "UsingTokenAuth": "%sスクリプト(crontab 等)でリクエストデータを得たい場合%sは、API をコールする URL(認証が必要)にパラメータ %s を付加する必要があります。", + "Glossary": "用語集", + "LearnAboutCommonlyUsedTerms": "Piwik Analytics の最もよく使われる用語について: %s と %s" } }
\ No newline at end of file diff --git a/plugins/API/lang/ko.json b/plugins/API/lang/ko.json index c2c8657960..2f04051378 100644 --- a/plugins/API/lang/ko.json +++ b/plugins/API/lang/ko.json @@ -5,7 +5,9 @@ "LoadedAPIs": "성공적으로 %s API를 불러옴", "MoreInformation": "Piwik API에 대한 자세한 내용은 %sIntroduction to Piwik API %s 문서와 %sPiwik API Reference%s 문서를 참조하세요.", "TopLinkTooltip": "JSON, XML 등의 간단한 API를 통해 프로그래밍 방식으로 웹 로그 분석 데이터에 접근할 수 있습니다.", - "UserAuthentication": "용자 인증", - "UsingTokenAuth": "%s 스크립트 (crontab 등)에서 요청 데이터를 얻고 싶다면 %s는 API를 호출하는 URL (인증 필요)에 매개 변수 %s를 추가해야합니다." + "UserAuthentication": "사용자 인증", + "UsingTokenAuth": "%s 스크립트 (crontab 등)에서 요청 데이터를 얻고 싶다면 %s는 API를 호출하는 URL (인증 필요)에 매개 변수 %s를 추가해야합니다.", + "Glossary": "용어", + "LearnAboutCommonlyUsedTerms": "Piwik 분석에 있어 자주 사용되는 용어 %s 와 %s 배우기" } }
\ No newline at end of file diff --git a/plugins/API/lang/nb.json b/plugins/API/lang/nb.json index 7cde5b4d1c..cd56984f85 100644 --- a/plugins/API/lang/nb.json +++ b/plugins/API/lang/nb.json @@ -1,6 +1,7 @@ { "API": { "LoadedAPIs": "Lastet %s API", - "UserAuthentication": "Brukerautentisering" + "UserAuthentication": "Brukerautentisering", + "Glossary": "Ordliste" } }
\ No newline at end of file diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index bca4396271..b42ccf2539 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -106,6 +106,7 @@ class Actions extends \Piwik\Plugin public function getJsFiles(&$jsFiles) { $jsFiles[] = "plugins/Actions/javascripts/actionsDataTable.js"; + $jsFiles[] = "plugins/Actions/javascripts/rowactions.js"; } public function isSiteSearchEnabled($idSites, $idSite) diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php index 5110130b84..7610e973ef 100644 --- a/plugins/Actions/Archiver.php +++ b/plugins/Actions/Archiver.php @@ -149,10 +149,7 @@ class Archiver extends \Piwik\Plugin\Archiver $select = "log_action.name, log_action.type, log_action.idaction, - log_action.url_prefix, - count(distinct log_link_visit_action.idvisit) as `" . PiwikMetrics::INDEX_NB_VISITS . "`, - count(distinct log_link_visit_action.idvisitor) as `" . PiwikMetrics::INDEX_NB_UNIQ_VISITORS . "`, - count(*) as `" . PiwikMetrics::INDEX_PAGE_NB_HITS . "`"; + log_action.url_prefix"; $select = $this->addMetricsToSelect($select, $metricsConfig); @@ -178,8 +175,7 @@ class Archiver extends \Piwik\Plugin\Archiver $rankingQuery = new RankingQuery($rankingQueryLimit); $rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW); $rankingQuery->addLabelColumn(array('idaction', 'name')); - $rankingQuery->addColumn(array('url_prefix', PiwikMetrics::INDEX_NB_UNIQ_VISITORS)); - $rankingQuery->addColumn(array(PiwikMetrics::INDEX_PAGE_NB_HITS, PiwikMetrics::INDEX_NB_VISITS), 'sum'); + $rankingQuery->addColumn('url_prefix'); if ($this->isSiteSearchEnabled()) { $rankingQuery->addColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min'); diff --git a/plugins/Actions/ArchivingHelper.php b/plugins/Actions/ArchivingHelper.php index b5735225b8..192a3089ba 100644 --- a/plugins/Actions/ArchivingHelper.php +++ b/plugins/Actions/ArchivingHelper.php @@ -53,7 +53,7 @@ class ArchivingHelper unset($row[PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT]); } - if ($row['type'] == Action::TYPE_CONTENT) { + if (in_array($row['type'], array(Action::TYPE_CONTENT, Action::TYPE_EVENT))) { continue; } diff --git a/plugins/Actions/Columns/ActionType.php b/plugins/Actions/Columns/ActionType.php new file mode 100644 index 0000000000..3d6e9b599a --- /dev/null +++ b/plugins/Actions/Columns/ActionType.php @@ -0,0 +1,70 @@ +<?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\Columns; + +use Piwik\Piwik; +use Piwik\Plugin\Dimension\ActionDimension; +use Piwik\Plugins\Actions\Segment; +use Piwik\Tracker\Action; +use Exception; + +/** + * This example dimension only defines a name and does not track any data. It's supposed to be only used in reports. + * + * See {@link http://developer.piwik.org/api-reference/Piwik/Columns\Dimension} for more information. + */ +class ActionType extends ActionDimension +{ + private $types = array( + Action::TYPE_PAGE_URL => 'pageviews', + Action::TYPE_CONTENT => 'contents', + Action::TYPE_SITE_SEARCH => 'sitesearches', + Action::TYPE_EVENT => 'events', + Action::TYPE_OUTLINK => 'outlinks', + Action::TYPE_DOWNLOAD => 'downloads' + ); + + /** + * The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app. + * @return string + */ + public function getName() + { + return Piwik::translate('Actions_ActionType'); + } + + protected function configureSegments() + { + $types = $this->types; + + $segment = new Segment(); + $segment->setSegment('actionType'); + $segment->setName('Actions_ActionType'); + $segment->setSqlSegment('log_action.type'); + $segment->setType(Segment::TYPE_METRIC); + $segment->setAcceptedValues(sprintf('A type of action, such as: %s', implode(', ', $types))); + $segment->setSqlFilter(function ($type) use ($types) { + if (array_key_exists($type, $types)) { + return $type; + } + + $index = array_search(strtolower(trim(urldecode($type))), $types); + + if ($index === false) { + throw new Exception("actionType must be one of: " . implode(', ', $types)); + } + + return $index; + }); + $segment->setSuggestedValuesCallback(function ($idSite, $maxSuggestionsToReturn) use ($types) { + return array_slice(array_values($types), 0, $maxSuggestionsToReturn); + }); + $this->addSegment($segment); + } +}
\ No newline at end of file diff --git a/plugins/Actions/Columns/ActionUrl.php b/plugins/Actions/Columns/ActionUrl.php new file mode 100644 index 0000000000..5fa80d1efa --- /dev/null +++ b/plugins/Actions/Columns/ActionUrl.php @@ -0,0 +1,32 @@ +<?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\Columns; + +use Piwik\Piwik; +use Piwik\Plugin\Dimension\ActionDimension; +use Piwik\Plugins\Actions\Segment; + +class ActionUrl extends ActionDimension +{ + public function getName() + { + return Piwik::translate('Actions_ColumnActionURL'); + } + + protected function configureSegments() + { + $segment = new Segment(); + $segment->setSegment('actionUrl'); + $segment->setName('Actions_ColumnActionURL'); + $segment->setUnionOfSegments(array('pageUrl', 'downloadUrl', 'outlinkUrl')); + + $this->addSegment($segment); + } + +} diff --git a/plugins/Actions/Metrics.php b/plugins/Actions/Metrics.php index 83b1c7370a..006fd6043d 100644 --- a/plugins/Actions/Metrics.php +++ b/plugins/Actions/Metrics.php @@ -50,6 +50,18 @@ class Metrics public static function getActionMetrics() { $metricsConfig = array( + PiwikMetrics::INDEX_NB_VISITS => array( + 'aggregation' => 'sum', + 'query' => "count(distinct log_link_visit_action.idvisit)" + ), + PiwikMetrics::INDEX_NB_UNIQ_VISITORS => array( + 'aggregation' => false, + 'query' => "count(distinct log_link_visit_action.idvisitor)" + ), + PiwikMetrics::INDEX_PAGE_NB_HITS => array( + 'aggregation' => 'sum', + 'query' => "count(*)" + ), PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION => array( 'aggregation' => 'sum', 'query' => "sum( diff --git a/plugins/Actions/Reports/GetPageUrls.php b/plugins/Actions/Reports/GetPageUrls.php index 2c850a5339..004d11ba88 100644 --- a/plugins/Actions/Reports/GetPageUrls.php +++ b/plugins/Actions/Reports/GetPageUrls.php @@ -39,7 +39,6 @@ class GetPageUrls extends Base new AveragePageGenerationTime() ); - $this->segmentSql = 'log_visit.visit_entry_idaction_url'; $this->subcategoryId = 'General_Pages'; } diff --git a/plugins/Actions/config/config.php b/plugins/Actions/config/config.php deleted file mode 100644 index 293acee476..0000000000 --- a/plugins/Actions/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Actions\Tracker\ActionsRequestProcessor'), - )), - -); diff --git a/plugins/Actions/javascripts/rowactions.js b/plugins/Actions/javascripts/rowactions.js new file mode 100644 index 0000000000..33050b90ed --- /dev/null +++ b/plugins/Actions/javascripts/rowactions.js @@ -0,0 +1,65 @@ +$(function () { + + function isActionsModule(params) + { + return params.module == 'Actions'; + } + + function isPageUrlReport(params) { + var action = params.action; + + return isActionsModule(params) && + (action == 'getPageUrls' || action == 'getEntryPageUrls' || action == 'getExitPageUrls' || action == 'getPageUrlsFollowingSiteSearch'); + }; + + function isPageTitleReport(params) { + var action = params.action; + + return isActionsModule(params) && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch'); + }; + + function getLinkForTransitionAndOverlayPopover(tr) + { + var link = tr.find('> td:first > a').attr('href'); + link = $('<textarea>').html(link).val(); // remove html entities + return link; + } + + DataTable_RowActions_Transitions.registerReport({ + isAvailableOnReport: function (dataTableParams) { + return isPageUrlReport(dataTableParams); + }, + isAvailableOnRow: function (dataTableParams, tr) { + return isPageUrlReport(dataTableParams) && tr.find('> td:first span.label').parent().is('a') + }, + trigger: function (tr, e, subTableLabel) { + var link = getLinkForTransitionAndOverlayPopover(tr); + this.openPopover('url:' + link); + } + }); + + DataTable_RowActions_Transitions.registerReport({ + isAvailableOnReport: function (dataTableParams) { + return isPageTitleReport(dataTableParams); + }, + isAvailableOnRow: function (dataTableParams, tr) { + return isPageTitleReport(dataTableParams); + }, + trigger: function (tr, e, subTableLabel) { + DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]); + } + }); + + DataTable_RowActions_Overlay.registerReport({ + isAvailableOnReport: function (dataTableParams) { + return isPageUrlReport(dataTableParams); + }, + onClick: function (actionA, tr, e) { + return { + link: getLinkForTransitionAndOverlayPopover(tr), + segment: null + } + } + }); + +});
\ No newline at end of file diff --git a/plugins/Actions/lang/cs.json b/plugins/Actions/lang/cs.json index 1c68478b02..5b63d05d60 100644 --- a/plugins/Actions/lang/cs.json +++ b/plugins/Actions/lang/cs.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Průměr dle %s přístupů %s mezi %s a %s", "ColumnClickedURL": "URL prokliku", + "ColumnActionURL": "URL akce", "ColumnClicks": "Prokliků", "ColumnClicksDocumentation": "Počet kliknutí na odkaz v závislosti na čase.", "ColumnDownloadURL": "URL stažení", @@ -32,17 +33,17 @@ "ExitPagesReportDocumentation": "Toto hlášení obsahuje informace o výstupních stránkách, které se vyskytly v daném období. Výstupní stránka je poslední stránkou, kterou uživatel zobrazí během své návštěvy. %s URL výstupních stránek jsou zobrazeny jako strom složek.", "ExitPageTitles": "Titulky výstupních stránek", "ExitPageTitlesReportDocumentation": "Toto hlášení obsahuje informace o titulcích výstupních stránek za dané období.", - "LearnMoreAboutSiteSearchLink": "Zjistěte více o sledování toho, jak návštěvníci používaí váš vyhledávač.", + "LearnMoreAboutSiteSearchLink": "Zjistěte více o sledování toho, jak návštěvníci používají váš vyhledávač.", "OneSearch": "1 Vyhledávání", "OutlinkDocumentation": "Externí odkaz je odkaz, který vede z vašich stránek na jinou domén.", "OutlinksReportDocumentation": "Toto hlášení zobrazuje hyerarchický seznam URL externích odkazů, na které vaši návštěvníci klikli.", "PagesReportDocumentation": "Toto hlášení obsahuje informace o URL navštívených. %s Tabulka je organizována hyerarchicky jako strom složek.", "PageTitlesReportDocumentation": "Toto hlášení obsahuje informace o titulcích navštívených stránek. %s Titulek je HTML tag %s, který většina prohlížečů zobrazuje v titulku okna.", "PageUrls": "URL stránky", - "PluginDescription": "Podává hlášení o zobrazení a titulcích stránek. Umožňuje vám měřit váš interní vyhledávač. Automaticky sleduje kliky na externí odkazy a soubory ke stažení.", + "PluginDescription": "Podává hlášení o zobrazení a titulcích stránek. Umožňuje měřit váš interní vyhledávač. Automaticky sleduje kliky na externí odkazy a soubory ke stažení.", "SiteSearchCategories1": "Toto hlášení shrnuje kategorie, které návštěvníci vybrali při vyhledávání na stránkách.", "SiteSearchCategories2": "Například elektronické obchody mívají filtr kategorií, aby mohli uzákazníci zvolit, ve které kategorii bude provedeno hledání.", - "SiteSearchFollowingPagesDoc": "Když návštěvníci hledají na vašich stránkách, snaží se nalézt určitou stránku, obhsa, produkt, nebo službu. Toto hlášení zobrazuje stránky, na které bylo klikáno nejčastěji po interním vyhledávání. Jinak řečeno, jedná se o stránky, které byly nejvíce hledány návštěvníky, kteří už byli na vašich stránkách.", + "SiteSearchFollowingPagesDoc": "Když návštěvníci hledají na vašich stránkách, snaží se nalézt určitou stránku, obsah, produkt, nebo službu. Toto hlášení zobrazuje stránky, na které bylo klikáno nejčastěji po interním vyhledávání. Jinak řečeno, jedná se o stránky, které byly nejvíce hledány návštěvníky, kteří už byli na vašich stránkách.", "SiteSearchIntro": "Sledování vyhledávání na vašich stránkách je velmi efektivní způsob, jak se dozvědět více o tom, co vaši návštěvníci na stránkách hledají, jaký obsah by je mohl zajímat, jaké produkty by si mohli chtít koupit, a jak jim obecně zpříjemnit pobyt na vašich stránkách.", "SiteSearchKeyword": "Klíčová slova (Vyhledávání)", "SiteSearchKeywordsDocumentation": "Toto hlášení shrnuje klíčová slova, která byla použita návštěvníky při hledání pomocí interního vyhledávače.", diff --git a/plugins/Actions/lang/de.json b/plugins/Actions/lang/de.json index f52aaabfbe..ffefdf14be 100644 --- a/plugins/Actions/lang/de.json +++ b/plugins/Actions/lang/de.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Durchschnitt basierend auf %s Aufruf(en) %s zwischen %s und %s", "ColumnClickedURL": "Angeklickte URL", + "ColumnActionURL": "Aktions-URL", "ColumnClicks": "Klicks", "ColumnClicksDocumentation": "Anzahl der Klicks auf diesen Link.", "ColumnDownloadURL": "Download-URL", diff --git a/plugins/Actions/lang/el.json b/plugins/Actions/lang/el.json index edc949a3f3..a495f92138 100644 --- a/plugins/Actions/lang/el.json +++ b/plugins/Actions/lang/el.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Μέσος όρος βάσει των %s επισκέψεων %s μεταξύ %s και %s", "ColumnClickedURL": "URL επιλογής", + "ColumnActionURL": "Διεύθυνση URL ενέργειας", "ColumnClicks": "Κλικ", "ColumnClicksDocumentation": "Οι φορές που πατήθηκε αυτός ο σύνδεσμος.", "ColumnDownloadURL": "URL λήψης", diff --git a/plugins/Actions/lang/en.json b/plugins/Actions/lang/en.json index 5a31efee34..f176db870d 100644 --- a/plugins/Actions/lang/en.json +++ b/plugins/Actions/lang/en.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Average based on %s hit(s) %s between %s and %s", "ColumnClickedURL": "Clicked URL", + "ColumnActionURL": "Action URL", "ColumnClicks": "Clicks", "ColumnClicksDocumentation": "The number of times this link was clicked.", "ColumnDownloadURL": "Download URL", @@ -60,6 +61,7 @@ "WidgetPageUrlsFollowingSearch": "Pages Following a Site Search", "WidgetSearchCategories": "Search Categories", "WidgetSearchKeywords": "Site Search Keywords", - "WidgetSearchNoResultKeywords": "Search Keywords with No Results" + "WidgetSearchNoResultKeywords": "Search Keywords with No Results", + "ActionType": "Action Type" } }
\ No newline at end of file diff --git a/plugins/Actions/lang/it.json b/plugins/Actions/lang/it.json index 013467699f..d727a00bbc 100644 --- a/plugins/Actions/lang/it.json +++ b/plugins/Actions/lang/it.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Media basata su %s hits %s tra %s e %s", "ColumnClickedURL": "URL Cliccate", + "ColumnActionURL": "URL dell'azione", "ColumnClicks": "Click", "ColumnClicksDocumentation": "Numero di volte che questo link è stato cliccato.", "ColumnDownloadURL": "URL Download", diff --git a/plugins/Actions/lang/ja.json b/plugins/Actions/lang/ja.json index 2d9e44354e..c752f0ece4 100644 --- a/plugins/Actions/lang/ja.json +++ b/plugins/Actions/lang/ja.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "%s ~ %s の間の %s のヒット %s に基づく平均", "ColumnClickedURL": "クリックされた URL", + "ColumnActionURL": "アクション URL", "ColumnClicks": "クリック数", "ColumnClicksDocumentation": "このリンクがクリックされた回数", "ColumnDownloadURL": "ダウンロード URL", @@ -39,6 +40,7 @@ "PagesReportDocumentation": "これは訪問されたページURLについてのリポートです。%s 表は階層構造になっており、URLはフォルダーの階層で表示されています。", "PageTitlesReportDocumentation": "これは訪問されたページタイトルについてのリポートです。%s ページタイトルは多くのブラウザでウインドウのタイトルに表示されるHTMLの %s タグです。", "PageUrls": "ページURL", + "PluginDescription": "ページ ビューとページのタイトルについてレポートします。内部のウェブサイトの検索エンジンを測定できます。自動的に外部リンクとファイルのダウンロードのクリックを追跡します。", "SiteSearchCategories1": "このレポートは、訪問者がサイトで検索した時に選択したカテゴリの一覧です。", "SiteSearchCategories2": "例えば、eコマースサイトは一般的に\"カテゴリー\"のセレクトを持っていて、訪問者は製品の検索結果を特定のカテゴリーに絞り込みできます。", "SiteSearchFollowingPagesDoc": "訪問者は、特定のページ、コンテンツ、製品、またはサービスを見つけようとして、サイト内で検索を行います。このレポートは、サイト内で検索してから最も多くクリックされたページの一覧です。言い換えれば、サイト内で訪問者が最も検索したページの一覧ということです。", diff --git a/plugins/Actions/lang/ko.json b/plugins/Actions/lang/ko.json index b5ec63216b..fa9f7d2257 100644 --- a/plugins/Actions/lang/ko.json +++ b/plugins/Actions/lang/ko.json @@ -1,6 +1,7 @@ { "Actions": { "ColumnClickedURL": "조회한 URL", + "ColumnActionURL": "활동 URL", "ColumnClicks": "조회수", "ColumnClicksDocumentation": "이 링크를 클릭 한 횟수입니다.", "ColumnDownloadURL": "다운로드 URL", @@ -32,15 +33,18 @@ "ExitPageTitles": "이탈 페이지 제목", "ExitPageTitlesReportDocumentation": "이 보고서는 기간의 이탈 페이지 제목에 대한 정보입니다.", "LearnMoreAboutSiteSearchLink": "방문자가 검색기능을 사용하는 것을 추적하는 방법에 대해 자세히 알아보세요.", + "OneSearch": "검색 1", "OutlinkDocumentation": "외부 링크는 사이트 내에서 외부 사이트(다른 도메인)로의 링크입니다.", "OutlinksReportDocumentation": "이 보고서는 방문자가 클릭 한 외부 링크의 URL을 계층 구조 목록으로 표시합니다.", "PagesReportDocumentation": "이 보고서는 방문한 페이지 URL에 대한 정보입니다. %s 테이블은 계층 구조로 되어 있으며, URL은 폴더 구조로 표시됩니다.", "PageTitlesReportDocumentation": "이 보고서는 방문한 페이지 제목에 대한 정보입니다. %s 페이지 제목은 대부분의 브라우저 윈도우의 제목에 표시되는 HTML %s 태그입니다.", "PageUrls": "페이지 URL", + "PluginDescription": "페이지 뷰와 제목에 대한 보고서. 내부 웹사이트 검색 엔진을 측정할 수 있습니다. 외부 링크 클릭 및 파일 다운로드에 대해서도 자동으로 추적합니다.", "SiteSearchCategories1": "이 보고서는 웹사이트에서 검색했을 때 방문자가 선택한 카테고리를 나열합니다.", "SiteSearchCategories2": "예를 들면, 일반적인 전자상거래 사이트는 '카테고리'를 제공하는데 방문자는 특정 카테고리의 모든 제품을 검색하는 것으로 범위를 축소할 수 있습니다.", "SiteSearchFollowingPagesDoc": "방문자는 특정 페이지, 콘텐츠, 제품 또는 서비스를 찾으려고 웹사이트에서 검색을 시도합니다. 이 보고서는 사이트 내부에서 검색시도 후 가장 많이 클릭한 페이지 목록입니다. 즉, 이 페이지 목록은 대부분의 방문자가 사이트내 검색에 의해 방문한 것입니다.", "SiteSearchIntro": "방문자의 검색을 추적하는 것은 당신의 웹사이트를 좋게 만드는 매우 효과적인 방법입니다. 잠재 고객이 무엇을 찾고 있는지에 대해 자세히 알 수 있고, 이것으로 새로운 콘텐츠에 대한 아이디어를 찾는 데 도움을 받게 되며, 잠재 고객이 찾는 새로운 전자상거래 제품을 제공할 수 있게 됩니다. 그리고 방문자의 웹사이트 사용 경험을 대폭 향상시킵니다.", + "SiteSearchKeyword": "키워드 (사이트 검색)", "SiteSearchKeywordsDocumentation": "이 보고서에서는 방문자가 내부 검색 엔진에서 검색한 검색어를 나열합니다.", "SiteSearchKeywordsNoResultDocumentation": "이 보고서는 검색 결과를 반환하지 않은 검색어를 나열합니다: 어쩌면 검색 엔진 알고리즘을 향상 시키거나 방문자가 당신의 웹사이트에 (아직)없는 콘텐츠를 찾고 있는 것이겠죠?", "SubmenuPagesEntry": "진입 페이지", diff --git a/plugins/Actions/lang/nb.json b/plugins/Actions/lang/nb.json index bf3f0ea718..18ab7471b9 100644 --- a/plugins/Actions/lang/nb.json +++ b/plugins/Actions/lang/nb.json @@ -1,41 +1,63 @@ { "Actions": { "AvgGenerationTimeTooltip": "Gjennomsnitt basert på %s treff %s mellom %s og %s", - "ColumnClickedURL": "URL for klikk", + "ColumnClickedURL": "Klikket URL", "ColumnClicks": "Klikk", "ColumnClicksDocumentation": "Antall klikk på denne lenken", - "ColumnDownloadURL": "URL for nedlasting", - "ColumnEntryPageTitle": "Tittel for inngangsside", - "ColumnEntryPageURL": "URL for inngangsside", - "ColumnExitPageTitle": "Tittel for utgangsside", - "ColumnExitPageURL": "URL for utgangsside", + "ColumnDownloadURL": "Nedlastings-URL", + "ColumnEntryPageTitle": "Tittel på inngangsside", + "ColumnEntryPageURL": "URL på inngangsside", + "ColumnExitPageTitle": "Tittel på utgangsside", + "ColumnExitPageURL": "URL på utgangsside", "ColumnNoResultKeyword": "Nøkkelord uten søkeresultat", "ColumnPageName": "Sidenavn", - "ColumnPagesPerSearch": "Søkeresultater", - "ColumnPageURL": "URL for side", + "ColumnPagesPerSearch": "Søkeresultatsider", + "ColumnPagesPerSearchDocumentation": "Brukere vil søke på ditt nettsted, og noen ganger klikke «neste» for å se flere resultater. Dette er det gjennomsnittlige antallet søkeresultatsider for dette nøkkelordet.", + "ColumnPageURL": "Side-URL", "ColumnSearchCategory": "Søkekategori", "ColumnSearches": "Søk", - "ColumnSearchesDocumentation": "Antall besøkende som søkte etter dette nøkkelordet i din søkemotor.", + "ColumnSearchesDocumentation": "Antall besøkende som søkte etter dette nøkkelordet i ditt nettsteds søkemotor.", + "ColumnSearchExits": "% Utganger fra søk", "ColumnSearchExitsDocumentation": "Antall besøkende som forlot nettsiden etter å ha søkt etter dette nøkkelordet.", "ColumnSearchResultsCount": "Antall søkeresultater", "ColumnSiteSearchKeywords": "Unike nøkkelord", "ColumnUniqueClicks": "Unike klikk", + "ColumnUniqueClicksDocumentation": "Antall besøk som involverte et klikk på denne lenken. Hvis lenken ble klikket flere ganger i løpet av et besøk, blir det kun talt én gang.", "ColumnUniqueDownloads": "Unike nedlastinger", "ColumnUniqueOutlinks": "Unike utlenker", "DownloadsReportDocumentation": "I denne rapporten kan du se hvilke filer de besøkende har lastet ned. %s Hva Piwik teller som en nedlasting, er klikket på en nedlastingslink. Om nedlastingen ble fullført eller ikke er ikke kjent for Piwik.", + "EntryPagesReportDocumentation": "Denne rapporten inneholder informasjon om inngangssidene som ble brukt i løpet av den angitte perioden. En inngangsside er den første siden en bruker ser i løpet av besøket. %s Inngangs-URLen vises som en mappestruktur.", + "EntryPageTitles": "Inngangssidetitler", + "EntryPageTitlesReportDocumentation": "Denne rapporten inneholder informasjon om titler på inngangssider som ble brukt i løpet av den angitte perioden.", + "ExitPagesReportDocumentation": "Denne rapporten inneholder informasjon om utgangssidene som ble brukt i den angitte perioden. En utgangsside er den siste siden en bruker ser i løpet av besøket. %s Utgangs-URLene vises som en mappestruktur.", + "ExitPageTitles": "Titler på utgangssider", + "ExitPageTitlesReportDocumentation": "Denne rapporten inneholder informasjon om titler på utgangssider som ble brukt i den angitte perioden.", + "LearnMoreAboutSiteSearchLink": "Lær mer om hvordan du kan spore hvordan besøkere bruker din søkemotor.", "OneSearch": "1 søk", + "OutlinkDocumentation": "En utlenke er en lenke som leder besøkeren bort fra ditt nettsted (til et annet domene).", + "OutlinksReportDocumentation": "Denne rapporten viser en hierarkisk liste av utlenkers URL-er som ble klikket av dine besøkere.", + "PagesReportDocumentation": "Denne rapporten inneholder informasjon om side-URLer som har blitt besøkt. %s Tabellen er organisert hierarkisk, URL-ene vises som en mappestruktur.", + "PageTitlesReportDocumentation": "Denne rapporten inneholder informasjon om titler på sider som har blitt besøkt. %s Sidetittelen er HTML-taggen %s som de fleste nettlesere viser i vindustittelen.", + "PageUrls": "Side-URLer", + "PluginDescription": "Rapporter om sidevisninger org sidetitler. Lar deg måle ditt nettsteds interne søkemotor. Sporer klikk på eksterne lenker og nedlastinger automatisk.", + "SiteSearchCategories1": "Denne rapporten viser kategorier som besøkere velger når de søker på ditt nettsted.", + "SiteSearchCategories2": "For eksempel: e-handelsnettsteder har typisk en «kategori»-velger slik at besøkere kan begrense sine søk til kun produkter i en spesifikk kategori.", + "SiteSearchFollowingPagesDoc": "Når besøkere søker på ditt nettsted, leter de etter en bestemt side, innhold, produkt eller tjeneste. Denne rapporten lister opp sider som ble klikket mest etter et internt søk. Med andre ord, det er listen over sider som blir mest søkt etter av besøkere som allerede er på nettstedet.", + "SiteSearchIntro": "Å spore søk som besøkere gjør på dine nettsteder er en effektiv måte å lære mer om hva din målgruppe leter etter. Det kan hjelpe deg å finne ideer til nytt innhold, nye produkter og generelt å øke kvaliteten på dine besøkeres opplevelse av ditt nettsted.", "SiteSearchKeyword": "Nøkkelord (Sidesøk)", + "SiteSearchKeywordsDocumentation": "Denne rapporten lister opp nøkkelord som besøkere har søkt etter i nettstedets interne søkemotor.", + "SiteSearchKeywordsNoResultDocumentation": "Denne rapporten lister opp nøkkelord som ikke ga noen søkeresultater. Kanskje søkealgoritmen kan forbedres, eller kanskje dine besøkere ser etter innhold som ikke er på ditt nettsted (ennå)?", "SubmenuPagesEntry": "Inngangssider", "SubmenuPagesExit": "Utgangssider", "SubmenuPageTitles": "Sidetitler", "SubmenuSitesearch": "Sidesøk", - "WidgetEntryPageTitles": "Inngangssiders tittel", - "WidgetExitPageTitles": "Utgangs Side Tittler", + "WidgetEntryPageTitles": "Titler på inngangssider", + "WidgetExitPageTitles": "Titler på utgangssider", "WidgetPagesEntry": "Inngangssider", "WidgetPagesExit": "Utgangssider", - "WidgetPageTitles": "Side Tittler", - "WidgetPageTitlesFollowingSearch": "Side Tittel fulgt av ett sidesøk", - "WidgetPageUrlsFollowingSearch": "Sider etter et nettstedssøk", + "WidgetPageTitles": "Sidetitler", + "WidgetPageTitlesFollowingSearch": "Sidetittel fulgt av ett nettstedssøk", + "WidgetPageUrlsFollowingSearch": "Sider som følger etter et nettstedssøk", "WidgetSearchCategories": "Søkekategorier", "WidgetSearchKeywords": "Nøkkelord for sidesøk", "WidgetSearchNoResultKeywords": "Nøkkelord uten resultater" diff --git a/plugins/Actions/lang/pt-br.json b/plugins/Actions/lang/pt-br.json index 99baab291c..41d3f88a93 100644 --- a/plugins/Actions/lang/pt-br.json +++ b/plugins/Actions/lang/pt-br.json @@ -2,6 +2,7 @@ "Actions": { "AvgGenerationTimeTooltip": "Média baseada em %s visita(s) %s entre %s e %s", "ColumnClickedURL": "URL clicado", + "ColumnActionURL": "URL Ação", "ColumnClicks": "Cliques", "ColumnClicksDocumentation": "O número de vezes que link foi clicado.", "ColumnDownloadURL": "URL do Download", diff --git a/plugins/AnonymousPiwikUsageMeasurement b/plugins/AnonymousPiwikUsageMeasurement -Subproject 02dfdc4b8e744cf432fdcb91e3613d0f04ccc59 +Subproject 5ba37193bba2be01df462b61c724c212657c449 diff --git a/plugins/Contents/lang/cs.json b/plugins/Contents/lang/cs.json index 596ee07a03..7cbe4e4488 100644 --- a/plugins/Contents/lang/cs.json +++ b/plugins/Contents/lang/cs.json @@ -8,6 +8,9 @@ "ContentName": "Jméno obsahu", "ContentPiece": "Část obsahu", "ContentTarget": "Cíl obsahu", - "Contents": "Obsah" + "Contents": "Obsah", + "InteractionsMetricDocumentation": "Kolikrát bylo s blokem obsahu interagováno (t. j. kliknuto na banner nebo reklamu).", + "ImpressionsMetricDocumentation": "Počet zobrazení banneru nebo reklamy na stránkách.", + "InteractionRateMetricDocumentation": "Poměr impresí obsahu k interakcím." } }
\ No newline at end of file diff --git a/plugins/Contents/lang/de.json b/plugins/Contents/lang/de.json index cc42a8d6bb..61cb4074fa 100644 --- a/plugins/Contents/lang/de.json +++ b/plugins/Contents/lang/de.json @@ -8,6 +8,9 @@ "ContentName": "Inhaltsname", "ContentPiece": "Inhaltsteil", "ContentTarget": "Inhaltsziel", - "Contents": "Inhalte" + "Contents": "Inhalte", + "InteractionsMetricDocumentation": "Die Anzahl, wie häufig mit einem Inhalt interagiert wurde (z.B. durch einen Klick auf ein Banner oder eine Anzeige).", + "ImpressionsMetricDocumentation": "Die Anzahl, wie häufig ein Inhalt, z.b. ein Banner oder eine Anzeige, auf der Seite angezeigt wurden.", + "InteractionRateMetricDocumentation": "Verhältnis zwischen Impressionen des Inhalts und Interaktionen." } }
\ No newline at end of file diff --git a/plugins/Contents/lang/ja.json b/plugins/Contents/lang/ja.json index 56af2f6ea7..a15de7b042 100644 --- a/plugins/Contents/lang/ja.json +++ b/plugins/Contents/lang/ja.json @@ -1,5 +1,6 @@ { "Contents": { + "PluginDescription": "コンテンツやバナーの追跡では、ページのコンテンツの任意の部分(バナー広告、イメージ、任意項目) のパフォーマンス(ビュー、クリック数、CTR )を測定できます。", "Impressions": "インプレッション", "Interactions": "インタラクション", "Interaction": "インタラクション", @@ -7,6 +8,9 @@ "ContentName": "コンテンツ名", "ContentPiece": "コンテンツ要素", "ContentTarget": "コンテンツターゲット", - "Contents": "内容" + "Contents": "内容", + "InteractionsMetricDocumentation": "( 例えば、バナーや広告を「クリック」というような ) 相互作用をコンテンツブロックが受けた回数。", + "ImpressionsMetricDocumentation": "広告やバナーなどのコンテンツブロックがページに表示された回数。", + "InteractionRateMetricDocumentation": "相互作用に対するコンテンツの表示回数の比率。" } }
\ No newline at end of file diff --git a/plugins/Contents/lang/ko.json b/plugins/Contents/lang/ko.json new file mode 100644 index 0000000000..121856bf06 --- /dev/null +++ b/plugins/Contents/lang/ko.json @@ -0,0 +1,11 @@ +{ + "Contents": { + "Interactions": "상호 작용들", + "Interaction": "상호 작용", + "InteractionRate": "상호 작용 비율", + "ContentName": "콘텐츠 이름", + "ContentPiece": "콘텐츠 조각", + "ContentTarget": "콘텐츠 목표", + "Contents": "콘텐츠" + } +}
\ No newline at end of file diff --git a/plugins/CoreAdminHome/Commands/DeleteLogsData.php b/plugins/CoreAdminHome/Commands/DeleteLogsData.php index df4141edbb..a504ed2c9c 100644 --- a/plugins/CoreAdminHome/Commands/DeleteLogsData.php +++ b/plugins/CoreAdminHome/Commands/DeleteLogsData.php @@ -168,6 +168,10 @@ class DeleteLogsData extends ConsoleCommand private function askForDeleteConfirmation(InputInterface $input, OutputInterface $output) { + if ($input->getOption('no-interaction')) { + return true; + } + $helper = $this->getHelper('question'); $question = new ConfirmationQuestion('<comment>You are about to delete log data. This action cannot be undone, are you sure you want to continue? (Y/N)</comment> ', false); @@ -194,4 +198,4 @@ class DeleteLogsData extends ConsoleCommand $this->writeSuccessMessage($output, array("Table optimization finished.")); } -}
\ No newline at end of file +} diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php index 305506038a..0f9bf60702 100644 --- a/plugins/CoreAdminHome/CoreAdminHome.php +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreAdminHome; use Piwik\Db; use Piwik\Piwik; +use Piwik\ProxyHttp; use Piwik\Settings\UserSetting; /** @@ -26,7 +27,9 @@ class CoreAdminHome extends \Piwik\Plugin 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'UsersManager.deleteUser' => 'cleanupUser', - 'API.DocumentationGenerator.@hideExceptForSuperUser' => 'displayOnlyForSuperUser' + 'API.DocumentationGenerator.@hideExceptForSuperUser' => 'displayOnlyForSuperUser', + 'Template.jsGlobalVariables' => 'addJsGlobalVariables', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys' ); } @@ -57,10 +60,28 @@ class CoreAdminHome extends \Piwik\Plugin $jsFiles[] = "plugins/CoreAdminHome/javascripts/generalSettings.js"; $jsFiles[] = "plugins/CoreHome/javascripts/donate.js"; $jsFiles[] = "plugins/CoreAdminHome/javascripts/pluginSettings.js"; + $jsFiles[] = "plugins/CoreAdminHome/javascripts/protocolCheck.js"; } public function displayOnlyForSuperUser(&$hide) { $hide = !Piwik::hasUserSuperUserAccess(); } + + public function addJsGlobalVariables(&$out) + { + if (ProxyHttp::isHttps()) { + $isHttps = 'true'; + } else { + $isHttps = 'false'; + } + + $out .= "piwik.hasServerDetectedHttps = $isHttps;\n"; + } + + public function getClientSideTranslationKeys(&$translationKeys) + { + $translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectly'; + $translationKeys[] = 'CoreAdminHome_ProtocolNotDetectedCorrectlySolution'; + } } diff --git a/plugins/CoreAdminHome/Menu.php b/plugins/CoreAdminHome/Menu.php index b4621e2f5e..a149baa399 100644 --- a/plugins/CoreAdminHome/Menu.php +++ b/plugins/CoreAdminHome/Menu.php @@ -61,7 +61,7 @@ class Menu extends \Piwik\Plugin\Menu if (!Piwik::isUserIsAnonymous()) { $menu->addManageItem('CoreAdminHome_TrackingCode', $this->urlForAction('trackingCodeGenerator'), - $order = 10); + $order = 20); if (SettingsManager::hasUserPluginsSettingsForCurrentUser()) { $menu->addPersonalItem('CoreAdminHome_PluginSettings', diff --git a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js index 87b053e980..3c983deca8 100644 --- a/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js +++ b/plugins/CoreAdminHome/javascripts/jsTrackingGenerator.js @@ -302,7 +302,7 @@ $('#javascript-text>textarea,#image-tracking-text>textarea').click(function () { $(this).select(); }); - + // initial generation getSiteData( $('#js-tracker-website').attr('siteid'), diff --git a/plugins/CoreAdminHome/javascripts/protocolCheck.js b/plugins/CoreAdminHome/javascripts/protocolCheck.js new file mode 100644 index 0000000000..5bd4148e10 --- /dev/null +++ b/plugins/CoreAdminHome/javascripts/protocolCheck.js @@ -0,0 +1,43 @@ +/*! + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +$(document).ready(function () { + + if (!piwik || !location.protocol) { + return; + } + + if (!piwik.hasSuperUserAccess) { + // we show a potential notification only to super users + return; + } + + if (piwik.hasServerDetectedHttps) { + // https was detected, not needed to show a message + return; + } + + var isHttpsUsed = 0 === location.protocol.indexOf('https'); + + if (!isHttpsUsed) { + // not using https anyway, we do not show a message + return; + } + + var params = [ + '"config/config.ini.php"', + '"assume_secure_protocol=1"', + '"[General]"', + '<a href="?module=Proxy&action=redirect&url=https://piwik.org/faq/how-to-install/faq_98/" target="_blank">', + '</a>' + ]; + var message = _pk_translate('CoreAdminHome_ProtocolNotDetectedCorrectly') + " " + _pk_translate('CoreAdminHome_ProtocolNotDetectedCorrectlySolution', params); + + var UI = require('piwik/UI'); + var notification = new UI.Notification(); + notification.show(message, {context: 'warning'}); +}); diff --git a/plugins/CoreAdminHome/lang/cs.json b/plugins/CoreAdminHome/lang/cs.json index 8233b15931..136ef9770a 100644 --- a/plugins/CoreAdminHome/lang/cs.json +++ b/plugins/CoreAdminHome/lang/cs.json @@ -8,9 +8,9 @@ "ClickHereToOptIn": "Klikněte zde pro přihlášení.", "ClickHereToOptOut": "Klikněte zde pro vyloučení.", "CustomLogoFeedbackInfo": "Pokud přizpůsobíte logo Piwiku, možná by vás zajímalo, jak skrýt odkaz %s v horním menu, Pokud to chcete provést, zakažte plugin zpětné vazby na stránce %sSpravovat zásuvné moduly%s.", - "CustomLogoHelpText": "Můžete přizpůsobit logo Piwiku, které bude zobrazeno v uživatelském rozhraní a v e-mailových hlášeních.", + "CustomLogoHelpText": "Můžete přizpůsobit logo Piwiku, které bude zobrazeno v uživatelském rozhraní a v emailových hlášeních.", "DevelopmentProcess": "Přestože náš %sproces vývoje%s zahrnuje tisíce automatizovaných testů, beta testeři hrají klíčovou roli v naší politice nevýskytu chyb.", - "EmailServerSettings": "Nastavení e-mailového serveru", + "EmailServerSettings": "Nastavení emailového serveru", "ForBetaTestersOnly": "Pouze pro beta testery", "ImageTracking": "Sledování obrázkem", "ImageTrackingIntro1": "Pokud má návštěvník vypnutý JavaScript nebo nemůže být JavaScript použit, můžete využít obrázku k měření a sledování Vaší návštěvnosti.", @@ -58,7 +58,7 @@ "MenuDevelopment": "Vývoj", "OptOutComplete": "Vyloučení hotovo. Vaše návštěvy nebudou sledovány nástrojem webové analýzy.", "OptOutCompleteBis": "Poznámka: pokud smažete cookie, odstraníte vylučovací cookie nebo zmměníte počítač nebo prohlížeč, budete muset provést proceduru vyloučení znovu.", - "OptOutDntFound": "Nejste sledováni, protože váš prohlížeč hlásí, že si to nepřejete. Jedná se o nastavení prohlížeče, takže se nebudete moct přihlásit, dokud nezakážete funkci nesledovat.", + "OptOutDntFound": "Nejste sledováni, protože váš prohlížeč hlásí, že si to nepřejete. Jedná se o nastavení prohlížeče, takže se nebudete moci přihlásit, dokud nezakážete funkci nesledovat.", "OptOutExplanation": "Piwik se zaměřuje na poskytování soukromí na internetu. Pokud chcete dát svým návštěvníkům možnost, aby byli vyloučeni z webové analýzy Piwikem, můžete na nějakou stránku (třeba stránku o soukromí) umístit následující HTML kód.", "OptOutExplanationBis": "Tento kód zobrazí iframe s odkazem, který nastaví u návštěvníka vynechávací cookie. %s Klikněte zde%s pro zobrazení obsahu iframe.", "OptOutForYourVisitors": "Piwik vyloučení pro Vaše návštěvníky", @@ -70,8 +70,8 @@ "PluginSettingsIntro": "Zde můžete změnit nastavení pro následující zásuvné moduly třetích stran:", "PluginSettingsValueNotAllowed": "Hodnota pro pole \"%s\" zásuvného modulu \"%s\" není povolena", "PluginSettingsSaveFailed": "Nepodařilo se uložit nastavení zásuvného modulu", - "SendPluginUpdateCommunication": "Pokud bude k dispozici aktualizace pluginu, odeslat e-mail.", - "SendPluginUpdateCommunicationHelp": "Super uživatelům bude odeslán e-mail, když bude k dispozici aktualizace zásuvného modulu.", + "SendPluginUpdateCommunication": "Pokud bude k dispozici aktualizace pluginu, odeslat email.", + "SendPluginUpdateCommunicationHelp": "Super uživatelům bude odeslán email, pokud bude k dispozici aktualizace zásuvného modulu.", "StableReleases": "Piwik je důležitý nástroj pro měření, doporučujeme vždy používat nejnovější vydání. Pokud používáte nejnovější beta verzi a našli jste chyby, prosíme o jejich nahlášení %spřímo zde %s.", "LtsReleases": "LTS (verze s dlouhodobou podporou) dostávají pouze bezpečnostní a jiné opravy chyb.", "SystemPluginSettings": "Systémová nastavení zásuvných modulů", @@ -84,9 +84,9 @@ "ValidPiwikHostname": "Platné jméno hostitele Piwiku", "WithOptionalRevenue": "s volitelným příjmem", "YouAreOptedIn": "Aktuálně nejste vyloučen.", - "YouAreOptedOut": "Aktuálně jste vyloučen.", - "YouMayOptOut": "Zde se můžete rozhodnout, zda se smí ve Vašem prohlížeči ukládat jedinečná analytická data “ Cookies” , a zda umožníte provozovateli webové stránky shromažďovat a analyzovat různé statistické údaje.", - "YouMayOptOutBis": "Pokud jste se rozhodli že ne, klikněte na přiložený odkaz pro uložení deaktivačního Cookie ve Vašem prohlížeči.", + "YouAreOptedOut": "Aktuálně jste vyloučeni.", + "YouMayOptOut": "Zde se můžete zakázat uložení cookie s identifikačním číslem přiděleným vašemu počítači a tím zamezit provozovateli této webové stránky shromažďovat a analyzovat statistické údaje.", + "YouMayOptOutBis": "Pokud jste se rozhodli že ne, klikněte na přiložený odkaz pro uložení deaktivačního cookie ve svém prohlížeči.", "OptingYouOut": "Vylučování, prosím čekejte..." } }
\ No newline at end of file diff --git a/plugins/CoreAdminHome/lang/en.json b/plugins/CoreAdminHome/lang/en.json index 5fe5d64d39..25a85f6475 100644 --- a/plugins/CoreAdminHome/lang/en.json +++ b/plugins/CoreAdminHome/lang/en.json @@ -87,6 +87,8 @@ "YouAreOptedOut": "You are currently opted out.", "YouMayOptOut": "You may choose not to have a unique web analytics cookie identification number assigned to your computer to avoid the aggregation and analysis of data collected on this website.", "YouMayOptOutBis": "To make that choice, please click below to receive an opt-out cookie.", - "OptingYouOut": "Opting you out, please wait..." + "OptingYouOut": "Opting you out, please wait...", + "ProtocolNotDetectedCorrectly": "You are currently viewing Piwik over a secure SSL connection (using https), but Piwik could only detect a non secure connection on the server. ", + "ProtocolNotDetectedCorrectlySolution": "To make sure Piwik securely requests and serves your content over HTTPS, you may edit your %s file and either configure your proxy settings, or you may add the line %s below the %s section. %sLearn more%s" } } diff --git a/plugins/CoreAdminHome/lang/it.json b/plugins/CoreAdminHome/lang/it.json index a8692d08a6..d453a6252c 100644 --- a/plugins/CoreAdminHome/lang/it.json +++ b/plugins/CoreAdminHome/lang/it.json @@ -56,7 +56,7 @@ "MenuGeneralSettings": "Impostazioni generali", "MenuManage": "Gestione", "MenuDevelopment": "Sviluppo", - "OptOutComplete": "Opt-out completato; le tue visite a questo sito non verranno registrate dallo strumento di Web Analytics.", + "OptOutComplete": "Opt-out completato. Le tue visite a questo sito non verranno registrate dallo strumento di Web Analytics.", "OptOutCompleteBis": "Nota che se cancelli i tuoi cookie, cancelli anche il cookie di opt-out, e se cambi computer o browser web, devi fare la procedura di opt-out nuovamente.", "OptOutDntFound": "Non vieni tracciato poiché il tuo browser comunica che non lo desideri. Questa è un'impostazione del tuo browser, dunque non potrai effettuare l'opt-in finchè non disabiliti la funzionalità \"Non Tracciare\".", "OptOutExplanation": "Piwik è impegnato ad assicurare la riservatezza su Internet. Per dare ai tuoi ospiti la possibilità di escludersi dalle Statistiche Web Piwik, è possibile aggiungere il seguente codice HTML in una pagina del tuo sito web, ad esempio in una pagina sulla privacy.", diff --git a/plugins/CoreAdminHome/lang/ja.json b/plugins/CoreAdminHome/lang/ja.json index 782ccc1ad7..a40ab8e3b8 100644 --- a/plugins/CoreAdminHome/lang/ja.json +++ b/plugins/CoreAdminHome/lang/ja.json @@ -1,8 +1,10 @@ { "CoreAdminHome": { + "AddNewTrustedHost": "新しい信頼できるホストを追加します。", "Administration": "管理", "ArchivingSettings": "アーカイブ設定", "BrandingSettings": "ブランディング設定", + "ReleaseChannel": "リリース チャネル", "ClickHereToOptIn": "クリックしてオプトイン。", "ClickHereToOptOut": "クリックしてオプトアウト。", "CustomLogoFeedbackInfo": "Piwikのロゴをカスタマイズする場合、トップメニューの %s のリンクも隠したいと思うかもしれません。それには %sプラグインの管理%s のページでフィードバックプラグインを無効にします。", @@ -68,8 +70,10 @@ "PluginSettingsIntro": "ここでは、次のサードパーティのプラグインの設定変更ができます", "PluginSettingsValueNotAllowed": "\"%s\" プラグインの \"%s\" フィールドの値は許可されていません。", "PluginSettingsSaveFailed": "プラグインの設定を保存できませんでした", + "SendPluginUpdateCommunication": "プラグインの更新が利用可能なときにメールを送信します。", "SendPluginUpdateCommunicationHelp": "使用可能な新しいプラグインのバージョンがある場合、管理者ユーザーへメールが通知されます。", "StableReleases": "Piwikがビジネスの重要な一部である場合、最新の安定版を使用することを推奨します。また、最新のベータ版を使用し、バグを見つけたり、提案があれば、%sこちらをご覧ください%s。", + "LtsReleases": "LTS (長期サポート) のバージョンは、セキュリティとバグ修正のみ受信します。", "SystemPluginSettings": "システムプラグイン設定", "TrackAGoal": "目標の追跡", "TrackingCode": "トラッキングコード", diff --git a/plugins/CoreAdminHome/lang/ko.json b/plugins/CoreAdminHome/lang/ko.json index d44b2797a7..d51f4e43be 100644 --- a/plugins/CoreAdminHome/lang/ko.json +++ b/plugins/CoreAdminHome/lang/ko.json @@ -8,22 +8,40 @@ "ClickHereToOptOut": "클릭하여 차단합니다.", "CustomLogoFeedbackInfo": "Piwik 로고를 변경하거나 상단 메뉴의 %s 링크도 숨길 수 있습니다. %s 플러그인 관리 %s 페이지에서 피드백 플러그인을 비활성화세요.", "CustomLogoHelpText": "Piwik 로고를 사용자정의하여 사용자 인터페이스 화면과 이메일 보고서를 볼 수 있습니다.", + "DevelopmentProcess": "Piwik의 %s개발 과정%s안에 수많은 자동 검사를 수행하지만, Piwik의 \"무버그 정책\"을 만족시키기 위해 베타 테스터가 상당히 중요합니다.", "EmailServerSettings": "메일 서버 설정", + "ForBetaTestersOnly": "베타 테스터 전용", "ImageTracking": "이미지 추적", "ImageTrackingLink": "이미지 추적 링크", "ImportingServerLogs": "서버 로그 가져오기", "JavaScriptTracking": "자바스크립트 추적", + "JSTracking_CampaignKwdParam": "캠페인 키워드 파라메터", + "JSTracking_CampaignNameParam": "캠페인 이름 파라메터", + "JSTracking_CodeNote": "아래 코드는 당신의 웹사이트에서 모든 페이지의 %1$s 태그 바로 앞에 위치해야 합니다.", + "JSTracking_CustomCampaignQueryParamDesc": "중요: %1$s Piwik는 자동으로 구글 분석 파라메터를 감지합니다.%2$s", + "JSTracking_EnableDoNotTrack": "클라이언트에서 DoNotTrack 탐지 활성화", + "JSTrackingIntro3": "대부분의 웹사이트, 블로그, CMS 등에서 쉽게 사용할 수 있도록 플러그인 형태로 제공되고 있습니다. (%1$sPiwik 통합 플러그인 리스트%2$s 보기) 만약 플러그인이 제공되지 않는다면, 웹사이트 템플릿을 고쳐 \"footer\" 파일에 해당 코드를 넣어 해결할 수 있습니다.", + "JSTrackingIntro5": "만약 페이지 뷰에 대한 것 이상을 원할 경우, %1$sPiwik 자바스크립트 트래킹 문서%2$s 내 여러 가능한 함수 리스트를 참고하세요. 이 함수들을 통해 목표나 맞춤 변수, 상거래 주문 및 담겨져있기만 한 카트 등을 추적할 수 있습니다.", "LogoUpload": "업로드 할 로고 선택", + "FaviconUpload": "업로드 할 파비콘 선택", "MenuDiagnostic": "진단", "MenuGeneralSettings": "일반 설정", "MenuManage": "관리", + "MenuDevelopment": "개발", "OptOutComplete": "차단 완료; 당신의 방문한 이 웹사이트는 이제 웹 분석 도구에 기록되지 않습니다.", "OptOutCompleteBis": "Cookie를 삭제하여 차단 Cookie를 삭제하거나 컴퓨터 또는 브라우저를 변경 한 경우는 차단 절차를 다시 수행해야 한다는 점 유의하세요.", "OptOutExplanation": "Piwik은 인터넷에서 개인 정보 제공에 최선을 다하고 있습니다. 방문자에 Piwik 웹 분석의 차단 옵션을 제공하기 위해 웹 사이트의 1페이지 (개인 정보 보호 정책 페이지 등)에 다음 HTML 코드를 추가 할 수 있습니다.", "OptOutExplanationBis": "HTML 코드는 방문자의 브라우저에 차단 Cookie를 설정하는 Piwik 차단 링크를 포함 iFrame을 표시합니다. iFrame에서 표시되는 내용을 표시하려면 %s여기를 클릭%s합니다.", "OptOutForYourVisitors": "방문자의 Piwik 차단", "PiwikIsInstalledAt": "Piwik가 설치되어 있습니다", + "PersonalPluginSettings": "나만의 플러그인 설정", "PluginSettings": "플러그인 설정", + "PluginSettingsIntro": "다음 나열된 서드 파티 플러그인의 설정을 바꿀 수 있다:", + "PluginSettingsSaveFailed": "플러그인 설정 저장 실패", + "SendPluginUpdateCommunication": "플러그인 업데이트가 가능할 때 이메일 알림", + "SendPluginUpdateCommunicationHelp": "플러그인의 새로운 버전이 나타날 경우 수퍼 유저에게 메일로 알려집니다.", + "StableReleases": "만약 Piwik가 당신의 비지니스에 상당히 중요한 요소라면 최근 안정 버전을 사용하시는 것을 추천합니다. 만약 최근 베타 버전을 사용 중 버그를 찾으셨거나 제안하실 사항이 있으시다면, %s여기를 봐주세요%s.", + "LtsReleases": "LTS (Long Term Support, 오랜 기간동안 지원됨) 버전은 보안 및 버그 해결만 받습니다.", "SystemPluginSettings": "시스템 플러그인 설정", "TrackAGoal": "목표 추적", "TrackingCode": "추적 코드", diff --git a/plugins/CoreAdminHome/lang/nb.json b/plugins/CoreAdminHome/lang/nb.json index d0fa1a793a..cb4c8f07e1 100644 --- a/plugins/CoreAdminHome/lang/nb.json +++ b/plugins/CoreAdminHome/lang/nb.json @@ -1,15 +1,26 @@ { "CoreAdminHome": { + "AddNewTrustedHost": "Legg til en ny trygg tjener", "Administration": "Administrasjon", "ArchivingSettings": "Arkiveringsinnstillinger", "BrandingSettings": "Innstillinger for profilering", + "ReleaseChannel": "Versjonskanal", "ClickHereToOptIn": "Trykk her for å delta.", "ClickHereToOptOut": "Trykk her for å ikke delta.", + "CustomLogoFeedbackInfo": "Hvis du tilpasser Piwik-logoen vil du kanskje også skjule %s-lenken i toppmenyen. For å gjøre det må du deaktivere Feedback-utvidelsen i %sAdministrasjon av utvidelser%s-siden.", + "CustomLogoHelpText": "Du kan tilpasse Piwik-logoen som vises i brukergrensesnittet og i e-postrapporter.", + "DevelopmentProcess": "Selv om vår %sutviklingsprosess%s inkluderer tusener av automatiske testser, spiller betatestere en viktig rolle for å at vi skal kunne etterleve vårt mål om å ikke ha noen feil.", "EmailServerSettings": "Innstillinger for e-posttjener", "ForBetaTestersOnly": "Kun for beta testere", "ImageTracking": "Bildesporing", - "ImageTrackingLink": "Sporings lenke for bilde", + "ImageTrackingIntro1": "Når en bruker har deaktivert JavaScript, eller når JavaScript ikke kan brukes, kan du bruke en bildesporingslenke for å spore besøkere.", + "ImageTrackingIntro2": "Generer lenken under og kopier den genererte HTML-koden til ditt nettsted. Hvis du bruker dette som en fallback for JavaScript-sporing, kan du bruke %1$s-tagger rundt koden.", + "ImageTrackingIntro3": "For hele listen med valg du kan bruke med en bildesporingslenke, se %1$sDokumentasjonen for sporings-API%2$s.", + "ImageTrackingLink": "Bildesporingslenke", "ImportingServerLogs": "Importerer serverlogger", + "ImportingServerLogsDesc": "Som et alternativ til å spore besøkere gjennom nettleseren (enten via JavaScript eller bildesporingslenke), kan du importere serverlogger regelmessig. %1$sLær mer serverlogganalyse her.%2$s", + "InvalidPluginsWarning": "De følgende utvidelsene er ikke kompatible med %1$s og kunne ikke lastes inn: %2$s.", + "InvalidPluginsYouCanUninstall": "Du kan oppdatere eller avinstallere disse utvidelsene på %1$sAdministrer utvidelser%2$s-siden.", "JavaScriptTracking": "JavaScript-sporing", "JSTracking_EnableDoNotTrack": "Aktiver klientside DoNotTrack-gjenkjenning", "LogoUpload": "Velg en logo for å laste opp", diff --git a/plugins/CoreAdminHome/lang/pt-br.json b/plugins/CoreAdminHome/lang/pt-br.json index bda01821fc..dd8298be31 100644 --- a/plugins/CoreAdminHome/lang/pt-br.json +++ b/plugins/CoreAdminHome/lang/pt-br.json @@ -48,10 +48,10 @@ "JSTrackingIntro4": "Se você não quiser usar JavaScript para monitorar visitantes, %1$sgere abaixo, um link para rastreamento por imagem%2$s.", "JSTrackingIntro5": "Se você deseja mais do que apenas monitorar exibições de página, porfavor verifique a %1$sdocumentação do rastreamento por javascript do Piwik%2$s para uma lista de funcões disponíveis. Usando essas funções você pode acompanhar objetivos, variáveis personalizadas, ordens de comércio eletrônico, compras abandonadas e muito mais.", "LogoNotWriteableInstruction": "Para usar um logotipo cutomizado ao invés do logo padrão Piwiki, dê permissão de escrita para esse diretório: %1$s Piwiki precisa de acesso de escrita para seus logotipos armazenados no arquivo %2$s.", - "FileUploadDisabled": "O envio de arquivos não está habilitado na sua configuração do PHP. Para enviar uma logomarca customizada defina %s no php.ini e reinicie seu servidor web.", + "FileUploadDisabled": "O carregamento de arquivos não está habilitado na sua configuração do PHP. Para carregar uma logomarca customizada defina %s no php.ini e reinicie seu servidor web.", "LogoUpload": "Selecione um logotipo para carregar", - "FaviconUpload": "Selecione um Favicon para fazer upload", - "LogoUploadHelp": "Por favor faça o upload de um arquivo nos formatos %s com uma altura mínima de %s pixels.", + "FaviconUpload": "Selecione um Favicon para carregar", + "LogoUploadHelp": "Por favor carregue um arquivo nos formatos %s com uma altura mínima de %s pixels.", "MenuDiagnostic": "Diagnostico", "MenuGeneralSettings": "Configurações Gerais", "MenuManage": "Gerenciar", diff --git a/plugins/CoreAdminHome/lang/tr.json b/plugins/CoreAdminHome/lang/tr.json index 1a4116f46e..8a412db6af 100644 --- a/plugins/CoreAdminHome/lang/tr.json +++ b/plugins/CoreAdminHome/lang/tr.json @@ -50,8 +50,12 @@ "OptOutForYourVisitors": "ziyaretciniz için Piwiki devre dışı bırakmak", "PiwikIsInstalledAt": "Piwikde yüklenir", "PluginSettingChangeNotAllowed": "Bu ayarların \"%s\" değerlerini değiştirmek için \"%s\" eklentisinde gerekli izniniz yok.", + "PluginSettings": "Eklenti Ayarları", "PluginSettingsIntro": "Buradan aşağıdaki 3. parti eklentiler için ayarları değiştirebilirsiniz:", + "PluginSettingsSaveFailed": "Eklenti ayarları kaydedilemedi", + "SendPluginUpdateCommunication": "Eklenti güncellemesi olduğunda e-posta gönder", "StableReleases": "Eğer Piwik işiniz için kritik bir parçaysa, son ve stabil olan versiyonu kullanmanızı öneririz. Eğer en son güncel betayı kullanıyorsanız ve bir hata bulur ya da öneriniz olursa, lütfen şuraya%s bakın%s.", + "SystemPluginSettings": "Sistem Eklenti Ayarları", "TrackAGoal": "Bir hedef izleme", "TrackingCode": "İzleme Kodu", "TrustedHostConfirm": "Güvenilir alan adınızı değiştirmek istediğinizden emin misiniz?", diff --git a/plugins/CoreAdminHome/stylesheets/generalSettings.less b/plugins/CoreAdminHome/stylesheets/generalSettings.less index 9d232f789b..dde1da48ed 100644 --- a/plugins/CoreAdminHome/stylesheets/generalSettings.less +++ b/plugins/CoreAdminHome/stylesheets/generalSettings.less @@ -8,7 +8,7 @@ } // hide PHP is deprecated notification in UI test -.uiTest [notification-id="PHP53VersionCheck"] { +.uiTest [notification-id="DeprecatedPHPVersionCheck"] { display: none !important; } diff --git a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig index 25d0352733..6d66695552 100644 --- a/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig +++ b/plugins/CoreAdminHome/templates/trackingCodeGenerator.twig @@ -75,7 +75,7 @@ {{ 'CoreAdminHome_JSTracking_MergeAliasesDesc'|translate("<span class='current-site-alias'>"~defaultReportSiteAlias~"</span>")|raw }} </div> <label class="checkbox"> - <input type="checkbox" id="javascript-tracking-all-aliases"/> + <input type="checkbox" checked="checked" id="javascript-tracking-all-aliases"/> {{ 'CoreAdminHome_JSTracking_MergeAliases'|translate }} <span class='current-site-name'>{{ defaultReportSiteName|raw }}</span> </label> diff --git a/plugins/CoreConsole/Commands/GenerateArchiver.php b/plugins/CoreConsole/Commands/GenerateArchiver.php index bbbdb85dfc..29bcee7bb6 100644 --- a/plugins/CoreConsole/Commands/GenerateArchiver.php +++ b/plugins/CoreConsole/Commands/GenerateArchiver.php @@ -30,7 +30,7 @@ class GenerateArchiver extends GeneratePluginBase $this->checkAndUpdateRequiredPiwikVersion($pluginName, $output); $exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin'; - $replace = array('ExamplePlugin' => ucfirst($pluginName), 'EXAMPLEPLUGIN' => strtoupper($pluginName)); + $replace = array('ExamplePlugin' => $pluginName, 'EXAMPLEPLUGIN' => strtoupper($pluginName)); $whitelistFiles = array('/Archiver.php'); $this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles); diff --git a/plugins/CoreConsole/Commands/GeneratePlugin.php b/plugins/CoreConsole/Commands/GeneratePlugin.php index c42d7a619b..bf83889305 100644 --- a/plugins/CoreConsole/Commands/GeneratePlugin.php +++ b/plugins/CoreConsole/Commands/GeneratePlugin.php @@ -12,6 +12,7 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\Filesystem; use Piwik\Plugins\ExamplePlugin\ExamplePlugin; use Piwik\Version; +use Piwik\Plugin; use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -28,7 +29,8 @@ class GeneratePlugin extends GeneratePluginBase ->setDescription('Generates a new plugin/theme including all needed files') ->addOption('name', null, InputOption::VALUE_REQUIRED, 'Plugin name ([a-Z0-9_-])') ->addOption('description', null, InputOption::VALUE_REQUIRED, 'Plugin description, max 150 characters') - ->addOption('pluginversion', null, InputOption::VALUE_OPTIONAL, 'Plugin version'); + ->addOption('pluginversion', null, InputOption::VALUE_OPTIONAL, 'Plugin version') + ->addOption('overwrite', null, InputOption::VALUE_NONE, 'Generate even if plugin directory already exists.'); } protected function execute(InputInterface $input, OutputInterface $output) @@ -115,20 +117,24 @@ class GeneratePlugin extends GeneratePluginBase */ protected function getPluginName(InputInterface $input, OutputInterface $output) { + $overwrite = $input->getOption('overwrite'); + $self = $this; - $validate = function ($pluginName) use ($self) { + $validate = function ($pluginName) use ($self, $overwrite) { if (empty($pluginName)) { throw new \RuntimeException('You have to enter a plugin name'); } - if (!Filesystem::isValidFilename($pluginName)) { - throw new \RuntimeException(sprintf('The plugin name %s is not valid', $pluginName)); + if (!Plugin\Manager::getInstance()->isValidPluginName($pluginName)) { + throw new \RuntimeException(sprintf('The plugin name %s is not valid. The name must start with a letter and is only allowed to contain numbers and letters.', $pluginName)); } $pluginPath = $self->getPluginPath($pluginName); - if (file_exists($pluginPath)) { + if (file_exists($pluginPath) + && !$overwrite + ) { throw new \RuntimeException('A plugin with this name already exists'); } diff --git a/plugins/CoreConsole/Commands/GeneratePluginBase.php b/plugins/CoreConsole/Commands/GeneratePluginBase.php index c3fce493dc..9012839943 100644 --- a/plugins/CoreConsole/Commands/GeneratePluginBase.php +++ b/plugins/CoreConsole/Commands/GeneratePluginBase.php @@ -31,7 +31,7 @@ abstract class GeneratePluginBase extends ConsoleCommand private function getRelativePluginPath($pluginName) { - return '/plugins/' . ucfirst($pluginName); + return '/plugins/' . $pluginName; } private function createFolderWithinPluginIfNotExists($pluginNameOrCore, $folder) @@ -325,8 +325,6 @@ abstract class GeneratePluginBase extends ConsoleCommand $validate($pluginName); } - $pluginName = ucfirst($pluginName); - return $pluginName; } diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php index bbe7e44c8f..a611ca8def 100644 --- a/plugins/CoreHome/Columns/UserId.php +++ b/plugins/CoreHome/Columns/UserId.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\CoreHome\Columns; use Piwik\Cache; use Piwik\DataTable; use Piwik\DataTable\Map; +use Piwik\Metrics; use Piwik\Period\Range; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; @@ -124,7 +125,14 @@ class UserId extends VisitDimension return false; } - $numUsers = $result->getColumn('nb_users'); + $firstRow = $result->getFirstRow(); + if ($firstRow instanceof DataTable\Row && $firstRow->hasColumn(Metrics::INDEX_NB_USERS)) { + $metric = Metrics::INDEX_NB_USERS; + } else { + $metric = 'nb_users'; + } + + $numUsers = $result->getColumn($metric); $numUsers = array_sum($numUsers); return !empty($numUsers); diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php index 519891acb0..c1f88e035c 100644 --- a/plugins/CoreHome/CoreHome.php +++ b/plugins/CoreHome/CoreHome.php @@ -143,6 +143,8 @@ class CoreHome extends \Piwik\Plugin $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/filters/htmldecode.js"; + $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/ucfirst.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js"; @@ -207,7 +209,6 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'General_Show'; $translationKeys[] = 'General_Hide'; $translationKeys[] = 'General_Website'; - $translationKeys[] = 'General_ChooseWebsite'; $translationKeys[] = 'Intl_Year_Short'; $translationKeys[] = 'General_MultiSitesSummary'; $translationKeys[] = 'General_SearchNoResults'; @@ -278,6 +279,7 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'Intl_Day_Min_StandAlone_6'; $translationKeys[] = 'Intl_Day_Min_StandAlone_7'; $translationKeys[] = 'General_And'; + $translationKeys[] = 'General_All'; $translationKeys[] = 'General_Search'; $translationKeys[] = 'General_Clear'; $translationKeys[] = 'General_MoreDetails'; @@ -304,5 +306,6 @@ class CoreHome extends \Piwik\Plugin $translationKeys[] = 'CoreHome_Segments'; $translationKeys[] = 'CoreHome_MenuEntries'; $translationKeys[] = 'SitesManager_Sites'; + $translationKeys[] = 'CoreHome_ChangeCurrentWebsite'; } } diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php index d73c5c2158..106685edf7 100644 --- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php +++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php @@ -91,7 +91,7 @@ class RowEvolution if (!is_array($this->label)) { throw new Exception("Expected label to be an array, got instead: " . $this->label); } - $this->label = $this->label[0]; + $this->label = Common::unsanitizeInputValue($this->label[0]); if ($this->label === '') throw new Exception("Parameter label not set."); diff --git a/plugins/CoreHome/Menu.php b/plugins/CoreHome/Menu.php index 20d9ac8eb8..c9331e4470 100644 --- a/plugins/CoreHome/Menu.php +++ b/plugins/CoreHome/Menu.php @@ -35,12 +35,13 @@ class Menu extends \Piwik\Plugin\Menu if (Piwik::isUserIsAnonymous()) { if (Plugin\Manager::getInstance()->isPluginActivated('ScheduledReports')) { - $menu->addItem($login, null, array('module' => 'ScheduledReports', 'action' => 'index'), 970, $login); + $menu->addItem($login, null, array('module' => 'ScheduledReports', 'action' => 'index'), 970, Piwik::translate('ScheduledReports_PersonalEmailReports')); } else { - $menu->addItem($login, null, array('module' => 'API', 'action' => 'listAllAPI'), 970, $login); + $menu->addItem($login, null, array('module' => 'API', 'action' => 'listAllAPI'), 970, Piwik::translate('API_ReportingApiReference')); } } else { - $menu->addItem($login, null, array('module' => 'UsersManager', 'action' => 'userSettings'), 970, $login); + $tooltip = sprintf('%s: %s', Piwik::translate('UsersManager_PersonalSettings'), $login); + $menu->addItem($login, null, array('module' => 'UsersManager', 'action' => 'userSettings'), 970, $tooltip); } $module = $this->getLoginModule(); diff --git a/plugins/CoreHome/angularjs/common/filters/htmldecode.js b/plugins/CoreHome/angularjs/common/filters/htmldecode.js new file mode 100644 index 0000000000..c3e8107931 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/filters/htmldecode.js @@ -0,0 +1,26 @@ +/*! + * 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('htmldecode', htmldecode); + + htmldecode.$inject = ['piwik']; + + /** + * Be aware that this filter can cause XSS so only use it when you're sure it is safe. + * Eg it should be safe when it is afterwards escaped by angular sanitize again. + */ + function htmldecode(piwik) { + + return function(text) { + if (text && text.length) { + return piwik.helper.htmlDecode(text); + } + + return text; + }; + } +})(); diff --git a/plugins/CoreHome/angularjs/common/filters/ucfirst.js b/plugins/CoreHome/angularjs/common/filters/ucfirst.js new file mode 100644 index 0000000000..34b3460be8 --- /dev/null +++ b/plugins/CoreHome/angularjs/common/filters/ucfirst.js @@ -0,0 +1,21 @@ +/*! + * 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('ucfirst', ucfirst); + + function ucfirst() { + + return function(value) { + if (!value) { + return value; + } + + var firstLetter = (value + '').charAt(0).toUpperCase(); + return firstLetter + value.substr(1); + }; + } +})(); diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html index 8cf6a9a67b..93371a03bf 100644 --- a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html +++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html @@ -1,6 +1,7 @@ -<ul class="navbar"> +<ul class="navbar"role="menu"> <li ng-repeat="category in menuModel.menu" class="menuTab" + role="menuitem" ng-class="{'active': category.active}"> <a class="item" href="" @@ -10,8 +11,9 @@ {{ 'CoreHome_Menu'|translate }} </span> </a> - <ul> + <ul role="menu"> <li ng-repeat="subcategory in category.subcategories" + role="menuitem" ng-class="{'active': subcategory.active}"> <div ng-if="subcategory.isGroup" piwik-menudropdown show-search="true" menu-title="{{ subcategory.name|escape }}"> <a class="item" diff --git a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html index 9ee4932163..e4e46a676a 100644 --- a/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html +++ b/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html @@ -16,7 +16,7 @@ <a ng-click="view.showSitesList=!view.showSitesList; view.showSitesList && !model.isLoading && model.loadInitialSites();" piwik-onenter="view.showSitesList=!view.showSitesList; view.showSitesList && !model.isLoading && model.loadInitialSites();" href="javascript:void(0)" - title="{{ 'General_ChooseWebsite'|translate }}" + title="{{ 'CoreHome_ChangeCurrentWebsite'|translate:((selectedSite.name || model.firstSiteName)|htmldecode) }}" ng-class="{'loading': model.isLoading}" class="title"> <span class="icon icon-arrow-bottom" @@ -37,6 +37,7 @@ ng-hide="!showSelectedSite && activeSiteId==site.idsite"> <a piwik-ignore-click href="{{ getUrlForSiteId(site.idsite) }}" piwik-autocomplete-matched="view.searchTerm" + title="{{ site.name|htmldecode }}" ng-bind-html="site.name"></a> </li> </ul> diff --git a/plugins/CoreHome/config/config.php b/plugins/CoreHome/config/config.php index 3c646cbd88..39236b62c5 100644 --- a/plugins/CoreHome/config/config.php +++ b/plugins/CoreHome/config/config.php @@ -3,10 +3,6 @@ return array( 'Piwik\Plugins\CoreHome\Tracker\VisitRequestProcessor' => DI\object() - ->constructorParameter('visitStandardLength', DI\get('ini.Tracker.visit_standard_length')), - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\CoreHome\Tracker\VisitRequestProcessor'), - )), + ->constructorParameter('visitStandardLength', DI\get('ini.Tracker.visit_standard_length')) ); diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js index 3d5fb0432c..30b21f46af 100644 --- a/plugins/CoreHome/javascripts/broadcast.js +++ b/plugins/CoreHome/javascripts/broadcast.js @@ -214,6 +214,10 @@ var broadcast = { currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr); } + if (module != 'CustomDimensions') { + currentHashStr = broadcast.updateParamValue('idDimension=', currentHashStr); + } + if (disableHistory) { var newLocation = window.location.href.split('#')[0] + '#?' + currentHashStr; // window.location.replace changes the current url without pushing it on the browser's history stack @@ -451,7 +455,8 @@ var broadcast = { broadcast.getParamValue('action', urlAjax), { idGoal: broadcast.getParamValue('idGoal', urlAjax), - idDashboard: broadcast.getParamValue('idDashboard', urlAjax) + idDashboard: broadcast.getParamValue('idDashboard', urlAjax), + idDimension: broadcast.getParamValue('idDimension', urlAjax) } ); }); diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js index 9a7f34cd4c..2ec3fb16d8 100644 --- a/plugins/CoreHome/javascripts/dataTable.js +++ b/plugins/CoreHome/javascripts/dataTable.js @@ -508,13 +508,20 @@ $.extend(DataTable.prototype, UIControl.prototype, { }; } + function getFilterLimitAsString(limit) { + if (limit == '-1') { + return _pk_translate('General_All').toLowerCase(); + } + return limit; + } + // setup limit control - $('.limitSelection', domElem).append('<div><span>' + self.param[limitParamName] + '</span></div><ul></ul>'); + $('.limitSelection', domElem).append('<div><span value="'+ self.param[limitParamName] +'">' + getFilterLimitAsString(self.param[limitParamName]) + '</span></div><ul></ul>'); if (self.props.show_limit_control) { $('.limitSelection ul', domElem).hide(); for (var i = 0; i < numbers.length; i++) { - $('.limitSelection ul', domElem).append('<li value="' + numbers[i] + '"><span>' + numbers[i] + '</span></li>'); + $('.limitSelection ul', domElem).append('<li value="' + numbers[i] + '"><span>' + getFilterLimitAsString(numbers[i]) + '</span></li>'); } $('.limitSelection ul li:last', domElem).addClass('last'); @@ -537,12 +544,12 @@ $.extend(DataTable.prototype, UIControl.prototype, { $('.limitSelection', domElem).is('.visible') ? hide() : show(); }); $('.limitSelection ul li', domElem).on('click', function (event) { - var limit = parseInt($(event.target).text()); + var limit = parseInt($(event.target).closest('li').attr('value')); hide(); if (limit != self.param[limitParamName]) { setLimitValue(self.param, limit); - $('.limitSelection>div>span', domElem).text(limit); + $('.limitSelection>div>span', domElem).text( getFilterLimitAsString(limit)).attr('value', limit); self.reloadAjaxDataTable(); var data = {}; @@ -1047,7 +1054,7 @@ $.extend(DataTable.prototype, UIControl.prototype, { $(this).attr('href', function () { var url = $(this).attr('href') + '&token_auth=' + piwik.token_auth; - var limit = $('.limitSelection>div>span', domElem).text(); + var limit = $('.limitSelection>div>span', domElem).attr('value'); var defaultLimit = $(this).attr('filter_limit'); if (!limit || 'undefined' === limit || defaultLimit == -1) { limit = defaultLimit; @@ -1071,6 +1078,7 @@ $.extend(DataTable.prototype, UIControl.prototype, { var segment = self.param.segment; var label = self.param.label; var idGoal = self.param.idGoal; + var idDimension = self.param.idDimension; var param_date = self.param.date; var date = $(this).attr('date'); if (typeof date != 'undefined') { @@ -1139,6 +1147,11 @@ $.extend(DataTable.prototype, UIControl.prototype, { && idGoal != '-1') { str += '&idGoal=' + idGoal; } + // Export Dimension specific reports + if (typeof idDimension != 'undefined' + && idDimension != '-1') { + str += '&idDimension=' + idDimension; + } if (label) { label = label.split(','); diff --git a/plugins/CoreHome/javascripts/dataTable_rowactions.js b/plugins/CoreHome/javascripts/dataTable_rowactions.js index 004e27e3b6..e7308fb93f 100644 --- a/plugins/CoreHome/javascripts/dataTable_rowactions.js +++ b/plugins/CoreHome/javascripts/dataTable_rowactions.js @@ -386,6 +386,17 @@ DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMeth requestParams.action = 'getRowEvolutionPopover'; requestParams.colors = JSON.stringify(piwik.getSparklineColors()); + var idDimension; + if (broadcast.getValueFromUrl('module') === 'Widgetize') { + idDimension = broadcast.getValueFromUrl('idDimension'); + } else { + idDimension = broadcast.getValueFromHash('idDimension'); + } + + if (idDimension) { + requestParams.idDimension = parseInt(idDimension, 10); + } + $.extend(requestParams, extraParams); var ajaxRequest = new ajaxHelper(); diff --git a/plugins/CoreHome/javascripts/notification.js b/plugins/CoreHome/javascripts/notification.js index e6c3f017f2..527c845d87 100644 --- a/plugins/CoreHome/javascripts/notification.js +++ b/plugins/CoreHome/javascripts/notification.js @@ -54,6 +54,16 @@ this.$node = placeNotification(template, options); }; + /** + * Removes a previously shown notification having the given notification id. + * + * + * @param {string} notificationId The id of a notification that was previously registered. + */ + Notification.prototype.remove = function (notificationId) { + $('[piwik-notification][notification-id=' + notificationId + ']').remove(); + }; + Notification.prototype.scrollToNotification = function () { if (this.$node) { piwikHelper.lazyScrollTo(this.$node, 250); diff --git a/plugins/CoreHome/javascripts/popover.js b/plugins/CoreHome/javascripts/popover.js index aff7ed7ff9..f694769821 100644 --- a/plugins/CoreHome/javascripts/popover.js +++ b/plugins/CoreHome/javascripts/popover.js @@ -142,7 +142,17 @@ var Piwik_Popover = (function () { /** Set the title of the popover */ setTitle: function (titleHtml) { + var titleText = piwikHelper.htmlDecode(titleHtml); + if (titleText.length > 60) { + titleHtml = $('<span>').attr('class', 'tooltip').attr('title', titleText).html(titleHtml); + } container.dialog('option', 'title', titleHtml); + try { + $('.tooltip', container.parentNode).tooltip('destroy'); + } catch (e) {} + if (titleText.length > 60) { + $('.tooltip', container.parentNode).tooltip({track: true, items: '.tooltip'}); + } }, /** Set inner HTML of the popover */ diff --git a/plugins/CoreHome/lang/cs.json b/plugins/CoreHome/lang/cs.json index b2a02bb644..aba489871a 100644 --- a/plugins/CoreHome/lang/cs.json +++ b/plugins/CoreHome/lang/cs.json @@ -20,7 +20,7 @@ "IncludeRowsWithLowPopulation": "Řádky s nízkou populací jsou skryty %s Zobrazit všechny řádky", "InjectedHostEmailBody": "Ahoj. Dnes jsem se snažil dostat do \\Piwiku a dostal jsem varování o neznámém ménu hostitele.", "InjectedHostEmailSubject": "K piwiku bylo přistupováno s neznámým jménem hostitele %s", - "InjectedHostNonSuperUserWarning": "%1$sKlikněte zde pro bezpečný přístup k Piwiku%2$s a odstranění tohoto varování. Pokud chcete upozornit administrátora na tento problém, %3$sklikněte zde pro odeslání e-mailu%4$s.", + "InjectedHostNonSuperUserWarning": "%1$sKlikněte zde pro bezpečný přístup k Piwiku%2$s a odstranění tohoto varování. Pokud chcete upozornit administrátora na tento problém, %3$sklikněte zde pro odeslání emailu%4$s.", "InjectedHostSuperUserWarning": "Piwik může být špatně nakonfigurován, například pokud byl přesunut na nový server nebo URL. Můžete %1$skliknout zde a přidat %2$s jako platné jméno hostitele Piwiku (pokud mu důvěřujete)%3$s, nebo %4$s klikněte zde%5$s pro bezpečný přístup k Piwiku%6$s.", "InjectedHostWarningIntro": "Nyní přistupujete k Piwiku z %1$s, ale byl nakonfigurován, aby naslouchal na následující adrese: %2$s.", "JavascriptDisabled": "Musíte mít zapnutý JavaScript, jinak Piwik nezobrazíte.<br \/> Nebo jen není Váš prohlížeč mezi podporovanými.<br \/>Pro běžné zobrazení zapněte JavaScript ve svém prohlížeči, poté %1$szkuste znovu%2$s.<br \/>", @@ -48,6 +48,11 @@ "YouAreUsingTheLatestVersion": "Používáte nejnovější verzi Piwiku.", "ClickRowToExpandOrContract": "Klikněte na tento řádek pro rozbalení nebo zbalení podtabulky.", "UndoPivotBySubtable": "Toto hlášení bylo zaměřeno %s Vrátit zpět", - "PivotBySubtable": "Toto hlášení není zaměřené %s Zaměřit na %s" + "PivotBySubtable": "Toto hlášení není zaměřené %s Zaměřit na %s", + "QuickAccessTitle": "Najít zástupce %s: Pro hledání stiskněte 'f'.", + "MenuEntries": "Položky menu", + "Segments": "Segmenty", + "AdblockIsMaybeUsed": "Pokud používáte blokovač reklam, zakažte ho pro tyto stránky, aby Piwik správně fungoval.", + "ChangeCurrentWebsite": "Vybrat webovou stránku, aktuálně vybraná stránka: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/de.json b/plugins/CoreHome/lang/de.json index 4c744998eb..a80015d88d 100644 --- a/plugins/CoreHome/lang/de.json +++ b/plugins/CoreHome/lang/de.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Suche nach %s. Tastenkürzel: Drücken Sie 'f' um zu suchen.", "MenuEntries": "Menüeinträge", "Segments": "Segmente", - "AdblockIsMaybeUsed": "Für den Fall, dass Sie einen Ad-Blocker verwenden, deaktivieren Sie diesen bitte für diese Seite um sicherzustellen, dass Piwik problemlos läuft." + "AdblockIsMaybeUsed": "Für den Fall, dass Sie einen Ad-Blocker verwenden, deaktivieren Sie diesen bitte für diese Seite um sicherzustellen, dass Piwik problemlos läuft.", + "ChangeCurrentWebsite": "Wählen Sie eine Webseite, aktuell gewählte Webseite: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/el.json b/plugins/CoreHome/lang/el.json index 242a7af9e6..ed3ae34bb2 100644 --- a/plugins/CoreHome/lang/el.json +++ b/plugins/CoreHome/lang/el.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Αναζήτηση για το %s. Πλήκτρο συντόμευσης: Πατήστε 'f' για αναζήτηση.", "MenuEntries": "Καταχωρήσεις μενού", "Segments": "Τμήματα", - "AdblockIsMaybeUsed": "Σε περίπτωση που χρησιμοποιείτε κάποια πρόγραμμα που μπλοκάρει τις διαφημίσεις, παρακαλούμε απενεργοποιήστε το για αυτόν τον ιστοτόπο για να είστε σίγουροι ότι το Piwik δουλεύει σωστά." + "AdblockIsMaybeUsed": "Σε περίπτωση που χρησιμοποιείτε κάποια πρόγραμμα που μπλοκάρει τις διαφημίσεις, παρακαλούμε απενεργοποιήστε το για αυτόν τον ιστοτόπο για να είστε σίγουροι ότι το Piwik δουλεύει σωστά.", + "ChangeCurrentWebsite": "Επιλέξτε ένα ιστοτόπο, επιλεγμένος ιστοτόπος αυτή τη στιγμή: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json index 6e38e1d0f2..75566a08f2 100644 --- a/plugins/CoreHome/lang/en.json +++ b/plugins/CoreHome/lang/en.json @@ -53,6 +53,7 @@ "QuickAccessTitle": "Search for %s. Shortcut: Press 'f' to search.", "MenuEntries": "Menu entries", "Segments": "Segments", - "AdblockIsMaybeUsed": "In case you are using an ad blocker, please disable it for this site to make sure Piwik works without any issues." + "AdblockIsMaybeUsed": "In case you are using an ad blocker, please disable it for this site to make sure Piwik works without any issues.", + "ChangeCurrentWebsite": "Choose a website, currently selected website: %s" } } diff --git a/plugins/CoreHome/lang/fr.json b/plugins/CoreHome/lang/fr.json index dc5980c335..c63eb35c23 100644 --- a/plugins/CoreHome/lang/fr.json +++ b/plugins/CoreHome/lang/fr.json @@ -48,6 +48,7 @@ "YouAreUsingTheLatestVersion": "Vous utilisez la dernière version de Piwik!", "ClickRowToExpandOrContract": "Cliquez sur cette rangée pour afficher ou masquer le sous-tableau", "UndoPivotBySubtable": "Ce rapport a été pivoté %s Annuler le pivot", - "PivotBySubtable": "Ce rapport n'a pas été pivoté %s Pivoter par %s" + "PivotBySubtable": "Ce rapport n'a pas été pivoté %s Pivoter par %s", + "Segments": "Segments" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/it.json b/plugins/CoreHome/lang/it.json index 1d1513c8de..37c9c5fc1a 100644 --- a/plugins/CoreHome/lang/it.json +++ b/plugins/CoreHome/lang/it.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Cerca %s. Shortcut: Premi 'f' per la ricerca.", "MenuEntries": "Ingressi da menù", "Segments": "Segmenti", - "AdblockIsMaybeUsed": "Se stai utilizzando un plugin di blocco della pubblicità, disabilitalo per questo sito, per essere sicuro che Piwik lavori senza problemi." + "AdblockIsMaybeUsed": "Se stai utilizzando un plugin di blocco della pubblicità, disabilitalo per questo sito, per essere sicuro che Piwik lavori senza problemi.", + "ChangeCurrentWebsite": "Scegli un sito, attualmente è selezionato: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/ja.json b/plugins/CoreHome/lang/ja.json index 6efb5237e9..d328182c53 100644 --- a/plugins/CoreHome/lang/ja.json +++ b/plugins/CoreHome/lang/ja.json @@ -48,6 +48,11 @@ "YouAreUsingTheLatestVersion": "Piwik の最新バージョンを使用しています !", "ClickRowToExpandOrContract": "サブテーブルを拡大または縮小するには、この列をクリックしてください。", "UndoPivotBySubtable": "このレポートは、%s 元に戻すピボットを旋回しました。", - "PivotBySubtable": "このレポートは、%s が %s ピボット を旋回していません。" + "PivotBySubtable": "このレポートは、%s が %s ピボット を旋回していません。", + "QuickAccessTitle": "%s を検索。ショートカット: ' f' キーを押して検索します。", + "MenuEntries": "メニュー エントリー", + "Segments": "セグメント", + "AdblockIsMaybeUsed": "広告ブロッカーを使用している場合には、Piwik が問題なく動作することを確認するために、このサイトでそれを無効にしてください。", + "ChangeCurrentWebsite": "ウェブサイトを選ぶ 現在選択されているWebサイト:%s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/ko.json b/plugins/CoreHome/lang/ko.json index f43ee7f5ae..aee10161d2 100644 --- a/plugins/CoreHome/lang/ko.json +++ b/plugins/CoreHome/lang/ko.json @@ -3,6 +3,7 @@ "CategoryNoData": "이 카테고리 안에 데이터가 없습니다. \"모든 집단 포함\"을 시도해보세요.", "CheckForUpdates": "업데이트 확인", "CheckPiwikOut": "Piwik 출시 확인!", + "ClickToEditX": "%s 수정하기", "CloseWidgetDirections": "위젯의 상단에있는 'X'아이콘을 클릭하여 이 위젯을 닫을 수 있습니다.", "DataForThisReportHasBeenPurged": "이 보고서의 데이터는 %s 개월 이상 되었기 때문에 제거되었습니다.", "DataTableExcludeAggregateRows": "요약 행 표시 %s 숨기기", @@ -13,6 +14,7 @@ "DonateCall3": "Piwik이 당신의 업무나 시도하는 일에 특별한 가치를 더해준다고 느끼신다면, %1$s기부해 주세요!%2$s", "DonateFormInstructions": "슬라이더를 클릭하여 금액을 선택하고, 기부에 기명을 클릭합니다.", "ExcludeRowsWithLowPopulation": "모든 행을 표시 %s 적은 량의 항목은 제외", + "ExternalHelp": "도움말 (새로운 탭에 열기)", "FlattenDataTable": "이 보고서는 계층형입니다. %s 펼치기", "HowMuchIsPiwikWorth": "당신에게 Piwik의 가치는 얼마인가요?", "IncludeRowsWithLowPopulation": "수가 적은 줄 숨기기 %s 모든 행을 표시", @@ -24,7 +26,9 @@ "JavascriptDisabled": "Piwik의 표준보기를 이용하려면 JavaScript가가 활성화되어 있어야 합니다.<br \/>하지만, 당신의 브라우저는 활성화되어있지 않거나 지원되지 않는 것 같습니다.<br \/>표준보기를 이용하려면 브라우저 옵션을 변경하여 JavaScript를 활성화하고 <br \/> %1$s다시 시도하세요%2$s.<br \/>", "MakeADifference": "개발 지원: Piwik 2.0에 %1$s기부해 주세요%2$s!", "MakeOneTimeDonation": "또는, 한 번만 기부합니다.", + "Menu": "메뉴", "NoPrivilegesAskPiwikAdmin": "'%s'로 로그인되었지만 권한이 설정되어 있지 않습니다. %s Piwik 관리자(클릭 이메일) %s에 '보기'권한을 부여 받으세요.", + "OnlyForSuperUserAccess": "해당 위젯은 유저가 수퍼 유저일 경우에만 나타납니다.", "PageOf": "%1$s \/ %2$s", "PeriodRange": "기간", "ReportGeneratedOn": "%s에 생성된 보고서", @@ -33,12 +37,20 @@ "SharePiwikShort": "Piwik은 오픈소스기반 무료 웹 분석 도구입니다. 이제 소중한 정보를 소유하세요!", "ShareThis": "공유하기", "ShowJSCode": "삽입할 자바스크립트 코드 보기", + "SkipToContent": "내용 건너뛰기", "SubscribeAndBecomePiwikSupporter": "Piwik의 후원자가 되기 위해서 보안 신용카드 결제 페이지 (페이팔)로 이동합니다!", "SupportPiwik": "Piwik 지원!", + "TableNoData": "이 테이블에 데이터가 없습니다.", "ThereIsNoDataForThisReport": "이 보고서에는 데이터가 없습니다.", "UnFlattenDataTable": "보고서가 펼쳐져 있습니다 %s 계층으로 표시하기", "ViewAllPiwikVideoTutorials": "모든 비디오 튜토리얼 보기", "WebAnalyticsReports": "웹 분석 보고서", - "YouAreUsingTheLatestVersion": "Piwik 최신 버전을 사용하고 있습니다!" + "YouAreUsingTheLatestVersion": "Piwik 최신 버전을 사용하고 있습니다!", + "ClickRowToExpandOrContract": "하위 테이블의 행을 확장하거나 축소하기 위해 클릭하세요.", + "QuickAccessTitle": "%s 검색하기. 단축키: 검색을 하려면 'f'를 누르세요.", + "MenuEntries": "메뉴", + "Segments": "세그멘트", + "AdblockIsMaybeUsed": "Piwik가 문제 없이 동작하기 위해서 광고 차단기능이 있는 플러그인을 꺼주시길 바랍니다.", + "ChangeCurrentWebsite": "웹사이트를 선택하세요, 현재 선택된 웹사이트: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/lt.json b/plugins/CoreHome/lang/lt.json index f0b88807bf..907f1bf178 100644 --- a/plugins/CoreHome/lang/lt.json +++ b/plugins/CoreHome/lang/lt.json @@ -3,6 +3,7 @@ "CategoryNoData": "Šioje kategorijoje duomenų nėra. Pabandykite \"Įtraukti visą populiaciją\"", "JavascriptDisabled": "JavaScript turi būti įjungta, kad būtų galima naudoti standartinę Piwik išvaizdą.<br \/>Tačiau panašu, kad JavaScript yra arba išjungta, arba nepalaikoma Jūsų naršyklės.<br \/>Norėdami matyti standartinę išvaizdą, įjunkite JavaScript keisdami savo naršyklės nustatymus, tada %1$sbandykite dar kartą%2$s.<br \/>", "PageOf": "%1$s iš %2$s", + "SharePiwikShort": "Piwik! Nemokama\/laisva saityno analitika. Būkite savo duomenų savininkais.", "ShowJSCode": "Parodyti JavaScript kodą įvedimui", "ThereIsNoDataForThisReport": "Nėra duomenų šiai ataskaitai.", "WebAnalyticsReports": "Žiniatinklio analizės sprendimo ataskaitos" diff --git a/plugins/CoreHome/lang/nb.json b/plugins/CoreHome/lang/nb.json index 91308236ff..33cc4649d0 100644 --- a/plugins/CoreHome/lang/nb.json +++ b/plugins/CoreHome/lang/nb.json @@ -1,12 +1,12 @@ { "CoreHome": { - "CategoryNoData": "Ingen data i denne kategorien. Prøv å velge \"Inkluder hele populasjon\".", + "CategoryNoData": "Ingen data i denne kategorien. Prøv å velge «Inkluder hele populasjonen».", "CheckForUpdates": "Se etter oppdateringer", "CheckPiwikOut": "Sjekk ut Piwik!", "ClickToEditX": "Klikk for å redigere %s", - "CloseWidgetDirections": "Du kan lukke dette elementet ved å klikke på X-ikonet på toppen av elementet.", + "CloseWidgetDirections": "Du kan lukke dette elementet ved å klikke på X-ikonet over widgeten.", "DataForThisReportHasBeenPurged": "Dataene for denne rapporten er mer enn %s måneder gamle og har blitt fjernet.", - "DataTableExcludeAggregateRows": "Aggregerte rader er vist som %s Skjul dem", + "DataTableExcludeAggregateRows": "Aggregerte rader vises %s Skjul dem", "DataTableIncludeAggregateRows": "Aggregerte rader er skjult %s Vi dem", "Default": "standard", "DonateCall1": "Det vil aldri koste noe å bruke Piwik, men det betyr ikke at det ikke koster oss noe å lage.", @@ -20,7 +20,7 @@ "IncludeRowsWithLowPopulation": "Rader med lav populasjon er skjult %s Vis alle rader", "InjectedHostEmailBody": "Hei, jeg prøvde å få tilgang til Piwik i dag og ble møtt av «ukjent vertsnavn»-advarselen.", "InjectedHostEmailSubject": "Piwik ble åpnet med et ukjent vertsnavn: %s", - "InjectedHostNonSuperUserWarning": "%1$sKlikk her for å få sikker tilgang til Piwik%2$s og for å fjerne denne advarselen. Du ømsker kanskje også å kontakte din Piwik-administrator for å varsle dem om dette problemet (%3$sklikk her for å sende epost%4$s).", + "InjectedHostNonSuperUserWarning": "%1$sKlikk her for å få sikker tilgang til Piwik%2$s og for å fjerne denne advarselen. Du vil kanskje også kontakte din Piwik-administrator for å varsle dem om dette problemet (%3$sklikk her for å sende e-post%4$s).", "InjectedHostSuperUserWarning": "Piwik kan være feilkonfigurert (for eksempel hvis Piwik nylig var flyttet til en ny tjener eller ny URL). Du kan enten %1$sklikke her og legge til %2$s som et gyldig Piwik-vertsnavn (hvis du stoler på det)%3$s eller %4$sklikk her og gå til %5$s for sikker tilgang til Piwik%6$s.", "InjectedHostWarningIntro": "Du har tilgang til Piwik fra %1$s, men Piwik er konfigurert til å kjøre på adressen: %2$s", "JavascriptDisabled": "JavaScript må være aktivert for at du skal kunne bruke Piwik i standardvisning.<br \/>Det ser ut til at JavaScript enten er deaktivert eller ikke støttet av din nettleser.<br \/>For å bruke standardvisning, må du aktivere JavaScript i nettleserinnstillingene og deretter %1$sprøve igjen%2$s.<br \/>", @@ -28,13 +28,13 @@ "MakeOneTimeDonation": "Bidra med en engangsdonasjon i stedet.", "Menu": "Meny", "NoPrivilegesAskPiwikAdmin": "Du er logget inn som «%s», men det ser ut til at du ikke har noen rettigheter satt i Piwik. %s Be din Piwik-administrator (klikk for å sende e-post)%s å gi deg «vis»-tilgang til et nettsted.", - "OnlyForSuperUserAccess": "Dette elementet er vises kun til brukere som har superbruker-tilgang.", + "OnlyForSuperUserAccess": "Denne widgeten vises kun til brukere som har superbruker-tilgang.", "PageOf": "%1$s av %2$s", - "PeriodRange": "Område", + "PeriodRange": "Periode", "ReportGeneratedOn": "Rapport generert %s", "ReportGeneratedXAgo": "Rapport generert %s siden", "SharePiwikLong": "Hei! Jeg har nettopp funnet et bra stykke fri programvare: Piwik!\n\nPiwik lar deg spore besøkende på ditt nettsted gratis. Du bør definitivt prøve det ut.", - "SharePiwikShort": "Piwik! Gratis og åpen kildekode web analyse. Du eier dataene.", + "SharePiwikShort": "Piwik! Gratis og åpen kildekode for nettstatistikk. Du eier dataene.", "ShareThis": "Del dette", "ShowJSCode": "Vis JavaScript-koden til å sette inn på din nettside.", "SkipToContent": "Hopp til innhold", @@ -48,6 +48,11 @@ "YouAreUsingTheLatestVersion": "Du bruker den nyeste versjonen av Piwik!", "ClickRowToExpandOrContract": "Klikk denne raden for utvide eller slå sammen undertabellen.", "UndoPivotBySubtable": "Denne rapporten er pivotert %s Angre pivot", - "PivotBySubtable": "Denne rapporten er ikke pivotert %s Pivot etter %s" + "PivotBySubtable": "Denne rapporten er ikke pivotert %s Pivot etter %s", + "QuickAccessTitle": "Søk etter %s. Snarvei: Trykk «f» for å søke.", + "MenuEntries": "Menyvalg", + "Segments": "Segmenter", + "AdblockIsMaybeUsed": "Hvis du bruker en ad-blocker, vennligst deaktiver den for dette nettstedet for å forsikre deg om at Piwik fungerer som det skal.", + "ChangeCurrentWebsite": "Velg et nettsted. Nettstedet som nå er valgt er: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/pt-br.json b/plugins/CoreHome/lang/pt-br.json index 69f02cb1ad..b7522980e0 100644 --- a/plugins/CoreHome/lang/pt-br.json +++ b/plugins/CoreHome/lang/pt-br.json @@ -52,6 +52,7 @@ "QuickAccessTitle": "Buscar por %s. Atalho: Tecle 'f' para buscar.", "MenuEntries": "Itens do menu", "Segments": "Segmentos", - "AdblockIsMaybeUsed": "Caso você esteja usando um bloqueador de anúncios, por favor desative-o para este site, pois assim poderá certificar-se de que o Piwik funciona sem qualquer problema." + "AdblockIsMaybeUsed": "Caso você esteja usando um bloqueador de anúncios, por favor desative-o para este site, pois assim poderá certificar-se de que o Piwik funciona sem qualquer problema.", + "ChangeCurrentWebsite": "Escolha um website; website atualmente selecionado: %s" } }
\ No newline at end of file diff --git a/plugins/CoreHome/lang/tr.json b/plugins/CoreHome/lang/tr.json index 572a8f8459..eddff9951e 100644 --- a/plugins/CoreHome/lang/tr.json +++ b/plugins/CoreHome/lang/tr.json @@ -31,7 +31,7 @@ "ShowJSCode": "Eklenecek JavaScript kodu göster", "SubscribeAndBecomePiwikSupporter": "Piwik Destekçisi olmak için güvenli bir kredi kartı ödeme sayfasına (Paypal) geçin!", "SupportPiwik": "Piwike destek ol", - "TableNoData": "Bu tablo için bir data yok.", + "TableNoData": "Bu tablo için bir veri yok.", "ThereIsNoDataForThisReport": "Bu rapor için veri yok.", "ViewAllPiwikVideoTutorials": "Tüm Piwik Yardım Videolarına Bak", "WebAnalyticsReports": "Web Analiz Raporları", diff --git a/plugins/CoreHome/stylesheets/coreHome.less b/plugins/CoreHome/stylesheets/coreHome.less index 9be7d14879..ab4b749b02 100644 --- a/plugins/CoreHome/stylesheets/coreHome.less +++ b/plugins/CoreHome/stylesheets/coreHome.less @@ -177,28 +177,24 @@ div.pk-emptyGraph { } .Piwik_Popover_Error_Title { - color: @theme-color-link; - font-weight: bold; font-size: 16px; } .Piwik_Popover_Error_Title span { - color: #222; font-weight: normal; font-size: 16px; } .Piwik_Popover_Error_Message { - color: #7e7363; padding: 20px 0 0 0; - font-size: 14px; + font-size: 13px; } a.Piwik_Popover_Error_Back { display: block; margin: 20px 0 0 0; - color: #1D3256; - font-size: 14px; + font-size: 13px; + text-decoration: underline; } #alert.ui-confirm input { diff --git a/plugins/CoreHome/stylesheets/layout.less b/plugins/CoreHome/stylesheets/layout.less index 5cffdf3be8..a398657c7c 100644 --- a/plugins/CoreHome/stylesheets/layout.less +++ b/plugins/CoreHome/stylesheets/layout.less @@ -30,7 +30,7 @@ height: 100%; transition: background-color 150ms linear; - &:hover { + &:hover, &:focus { background-color: @theme-color-menu-contrast-background; } @@ -59,7 +59,7 @@ .navbar { a { text-decoration: none; - &:hover, &:focus, &:active { + &:hover, &:focus { text-decoration: none; } } @@ -244,7 +244,7 @@ > .item { cursor: default; font-weight: bold; - &:hover { + &:hover, &:focus { text-decoration: none; } } @@ -257,7 +257,7 @@ padding: 11px 22px 11px 45px; decoration: none; transition: background-color 200ms linear; - &:hover { + &:hover, &:focus { text-decoration: none; color: @theme-color-menu-contrast-textActive; } @@ -300,7 +300,7 @@ font-size: 13px; font-weight: normal; - &:hover { + &:hover, &:focus { color: @theme-color-menu-contrast-textActive; } @@ -325,7 +325,7 @@ > .item { cursor: pointer; - &:hover { + &:hover, &:focus { color: @theme-color-menu-contrast-textActive; } } @@ -388,7 +388,7 @@ border: 1px solid @theme-color-background-tinyContrast; transition: box-shadow 150ms linear; &.expanded, - &:hover { + &:hover, &:focus { box-shadow: 0 1px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12); } } diff --git a/plugins/CoreHome/templates/_dataTable.twig b/plugins/CoreHome/templates/_dataTable.twig index 6bc35bca9e..697af2c9b6 100644 --- a/plugins/CoreHome/templates/_dataTable.twig +++ b/plugins/CoreHome/templates/_dataTable.twig @@ -12,7 +12,7 @@ data-params="{% if clientSideParameters is empty %}{}{% else %}{{ clientSideParameters|json_encode }}{% endif %}"> <div class="reportDocumentation"> {% if properties.documentation|default is not empty %}<p>{{ properties.documentation|raw }}</p>{% endif %} - {% if reportLastUpdatedMessage is defined %}<span class='helpDate'>{{ reportLastUpdatedMessage }}</span>{% endif %} + {% if reportLastUpdatedMessage is defined %}<span class='helpDate'>{{ reportLastUpdatedMessage|raw }}</span>{% endif %} </div> <div class="dataTableWrapper"> {% if error is defined %} diff --git a/plugins/CoreHome/templates/_menu.twig b/plugins/CoreHome/templates/_menu.twig index 51b87bc301..94321a033f 100644 --- a/plugins/CoreHome/templates/_menu.twig +++ b/plugins/CoreHome/templates/_menu.twig @@ -1,10 +1,9 @@ - {% macro menu(menu, anchorlink, cssClass, currentModule, currentAction) %} <div id="secondNavBar" class="{{ cssClass }}"> <div id="search" ng-cloak> <div piwik-quick-access class="borderedControl"></div> </div> - <ul class="navbar"> + <ul class="navbar" role="menu"> {% for level1,level2 in menu %} {% set hasSubmenuItem = false %} @@ -15,7 +14,7 @@ {% endfor %} {% if hasSubmenuItem %} - <li class="menuTab"> + <li class="menuTab" role="menuitem"> <a class="item"> <span class="menu-icon {{ level2._icon|default('icon-arrow-right') }}"></span>{{ level1|translate }} @@ -23,12 +22,14 @@ {{ 'CoreHome_Menu'|translate }} </span> </a> - <ul> + <ul role="menu"> {% for name,urlParameters in level2 %} {% if name|slice(0,1) != '_' %} <li {% if urlParameters._url.module is defined and urlParameters._url.module == currentModule and urlParameters._url.action is defined and urlParameters._url.action == currentAction %}class="active"{% endif %} - > + role="menuitem" + > <a class="item" + title="{{ urlParameters._tooltip|default(name)|translate|e('html_attr') }}" href="index.php?{{ urlParameters._url|urlRewriteWithParameters|slice(1) }}"> {{ name|translate }} </a> diff --git a/plugins/CoreHome/templates/_siteSelectHeader.twig b/plugins/CoreHome/templates/_siteSelectHeader.twig index 7353e2e07a..beacabd849 100644 --- a/plugins/CoreHome/templates/_siteSelectHeader.twig +++ b/plugins/CoreHome/templates/_siteSelectHeader.twig @@ -1,3 +1,3 @@ <div class="top_bar_sites_selector piwikTopControl"> - <div piwik-siteselector class="sites_autocomplete"></div> + <div piwik-siteselector show-selected-site="true" class="sites_autocomplete"></div> </div>
\ No newline at end of file diff --git a/plugins/CoreHome/templates/_topBar.twig b/plugins/CoreHome/templates/_topBar.twig index e69e9c1978..c1158f46bb 100644 --- a/plugins/CoreHome/templates/_topBar.twig +++ b/plugins/CoreHome/templates/_topBar.twig @@ -1,5 +1,5 @@ {{ postEvent("Template.beforeTopBar", userAlias, userLogin, topMenu, userMenu) }} -<ul class="navbar-right"> +<ul role="menubar" class="navbar-right"> {% macro menuItemLabel(label, icon) %} {% if icon is defined and icon and icon starts with 'icon-' %} @@ -27,7 +27,7 @@ {% spaceless %} {% for label,menu in topMenu %} - <li>{{ _self.topMenuItem(label, menu, topMenuModule, topMenuAction) }}</li> + <li role="menuitem">{{ _self.topMenuItem(label, menu, topMenuModule, topMenuAction) }}</li> {% endfor %} {% endspaceless %} diff --git a/plugins/CoreHome/tests/Integration/Column/UserIdTest.php b/plugins/CoreHome/tests/Integration/Column/UserIdTest.php index 33c268afae..70715a4dc2 100644 --- a/plugins/CoreHome/tests/Integration/Column/UserIdTest.php +++ b/plugins/CoreHome/tests/Integration/Column/UserIdTest.php @@ -12,6 +12,7 @@ use Piwik\Access; use Piwik\Cache; use Piwik\DataAccess\ArchiveTableCreator; use Piwik\Db; +use Piwik\Metrics; use Piwik\Plugin\Manager; use Piwik\Plugins\CoreHome\Columns\UserId; use Piwik\Tests\Framework\Fixture; @@ -164,6 +165,17 @@ class UserIdTest extends IntegrationTestCase $this->assertDataTableHasUsers($this->getDataTableWithUsers()); } + public function test_hasDataTableUsers_shouldBeAbleToDetectIfNbUsersMetricIdIsused() + { + $table = $this->getDataTableWithZeroUsers(); + $table->renameColumn('nb_users', Metrics::INDEX_NB_USERS); + $this->assertNotDataTableHasUsers($table); + + $table = $this->getDataTableWithUsers(); + $table->renameColumn('nb_users', Metrics::INDEX_NB_USERS); + $this->assertDataTableHasUsers($this->getDataTableWithUsers()); + } + private function getDataTableWithoutUsersColumn() { $tableWithoutUsers = new DataTable(); diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php index ea9a386ced..c9b4a48a7c 100644 --- a/plugins/CorePluginsAdmin/Controller.php +++ b/plugins/CorePluginsAdmin/Controller.php @@ -247,7 +247,7 @@ class Controller extends Plugin\ControllerAdmin $view->deactivateNonce = Nonce::getNonce(static::DEACTIVATE_NONCE); $view->pluginsInfo = $this->getPluginsInfo($themesOnly); - $users = \Piwik\Plugins\UsersManager\API::getInstance()->getUsers(); + $users = Request::processRequest('UsersManager.getUsers'); $view->otherUsersCount = count($users) - 1; $view->themeEnabled = \Piwik\Plugin\Manager::getInstance()->getThemeEnabled()->getPluginName(); diff --git a/plugins/CorePluginsAdmin/Marketplace.php b/plugins/CorePluginsAdmin/Marketplace.php index c4835856b0..b46d1932e4 100644 --- a/plugins/CorePluginsAdmin/Marketplace.php +++ b/plugins/CorePluginsAdmin/Marketplace.php @@ -114,14 +114,14 @@ class Marketplace $pluginsHavingUpdate = array(); } - foreach ($pluginsHavingUpdate as &$updatePlugin) { + foreach ($pluginsHavingUpdate as $key => $updatePlugin) { foreach ($loadedPlugins as $loadedPlugin) { if (!empty($updatePlugin['name']) && $loadedPlugin->getPluginName() == $updatePlugin['name'] ) { $updatePlugin['currentVersion'] = $loadedPlugin->getVersion(); $updatePlugin['isActivated'] = $pluginManager->isPluginActivated($updatePlugin['name']); - $updatePlugin = $this->addMissingRequirements($updatePlugin); + $pluginsHavingUpdate[$key] = $this->addMissingRequirements($updatePlugin); break; } } @@ -129,9 +129,7 @@ class Marketplace // remove plugins that have updates but for some reason are not loaded foreach ($pluginsHavingUpdate as $key => $updatePlugin) { - if (empty($updatePlugin['currentVersion']) - || empty($updatePlugin['isActivated']) - ) { + if (empty($updatePlugin['currentVersion'])) { unset($pluginsHavingUpdate[$key]); } } diff --git a/plugins/CorePluginsAdmin/lang/cs.json b/plugins/CorePluginsAdmin/lang/cs.json index fa4527c353..8ae77b996f 100644 --- a/plugins/CorePluginsAdmin/lang/cs.json +++ b/plugins/CorePluginsAdmin/lang/cs.json @@ -23,7 +23,7 @@ "Developer": "Vývojář", "DevelopersLearnHowToDevelopPlugins": "Vývojáři: naučte se, jak rozšířit Piwik vývojem %szásuvných modulů nebo témat vzhledu%s.", "DoMoreContactPiwikAdmins": "Pokud chcete nainstalovat nový zásuvný modul nebo šablonu,, kontaktujte vaše administrátory Piwiku.", - "EmailToEnquireUpdatedVersion": "Napište e-mail %1$s a požádejte o novější verzi %2$s.", + "EmailToEnquireUpdatedVersion": "Napište email %1$s a požádejte o novější verzi %2$s.", "FeaturedPlugin": "Doporučovaný zásuvný modul", "ChangeLookByManageThemes": "Vzhled Piwiku můžete změnit %sve správě motivů%s.", "GetEarlyAccessForPaidPlugins": "Poznámka: Momentálně jsou všechny zásuvné moduly v obchodě zdarma. V budoucnu máme v plánu povolit placené zásuvné moduly. %sKontaktujte nás%s pro včasný přístup.", diff --git a/plugins/CorePluginsAdmin/lang/id.json b/plugins/CorePluginsAdmin/lang/id.json index 100b8e195b..1e4ef37aaa 100644 --- a/plugins/CorePluginsAdmin/lang/id.json +++ b/plugins/CorePluginsAdmin/lang/id.json @@ -12,6 +12,7 @@ "PluginHomepage": "Alamat Pengaya", "PluginKeywords": "Kata Kunci", "PluginsManagement": "Pengatur Pengaya", + "PluginVersionInfo": "%1$s dari %2$s", "Status": "Status", "Version": "Versi", "Websites": "Situs" diff --git a/plugins/CorePluginsAdmin/lang/ja.json b/plugins/CorePluginsAdmin/lang/ja.json index 2da547a233..708b4a6560 100644 --- a/plugins/CorePluginsAdmin/lang/ja.json +++ b/plugins/CorePluginsAdmin/lang/ja.json @@ -38,12 +38,18 @@ "LastCommitTime": "(最終更新 %s)", "LastUpdated": "最終更新日", "LicenseHomepage": "ライセンスのホームページ", + "LikeThisPlugin": "このプラグインを気に入りましたか?", + "ConsiderDonating": "寄付を検討", + "CommunityContributedPlugin": "このプラグインは、コミュニティの貢献により無料で公開されています。", + "ConsiderDonatingCreatorOf": "%s の作成者への寄付を検討", "PluginsExtendPiwik": "プラグインは Piwik の機能性を拡張します。", "OncePluginIsInstalledYouMayActivateHere": "プラグインをインストールすると、ここで有効化と無効化を行うことができます。", "Marketplace": "マーケットプレイス", "MarketplaceSellPluginSubject": "マーケットプレイス - プラグインの販売", "MenuPlatform": "プラットフォーム", "MissingRequirementsNotice": "%1$s %3$s が必要です。 %1$s %2$s を最新バージョンにアップデートしてください。", + "MissingRequirementsPleaseInstallNotice": "%3$s で必要とされる、 %1$s %2$s をインストールしてください。", + "NewVersion": "新しいバージョン", "NoPluginsFound": "プラグインは見つかりませんでした", "NotAllowedToBrowseMarketplacePlugins": "Piwik プラットフォームのカスタマイズまたは拡張のために、インストール可能なプラグインのリストを閲覧することができます。もしこれらのいづれかのインストールが必要な場合は、管理者にお問い合わせください。", "NotAllowedToBrowseMarketplaceThemes": "Piwik プラットフォームの外観をカスタマイズするのにインストール可能な Piwik テーマのリストをご確認ください。これらのいづれかをインストールしたい場合は、管理者にお問い合わせください。", diff --git a/plugins/CorePluginsAdmin/lang/ko.json b/plugins/CorePluginsAdmin/lang/ko.json index 6949605d48..406ec44c78 100644 --- a/plugins/CorePluginsAdmin/lang/ko.json +++ b/plugins/CorePluginsAdmin/lang/ko.json @@ -1,19 +1,59 @@ { "CorePluginsAdmin": { + "ActionActivatePlugin": "플러그인 활성화", + "ActionActivateTheme": "테마 활성화", + "ActionInstall": "설치", + "ActionUninstall": "제거", "Activate": "활성화", "Activated": "활성화됨", "Active": "활성", + "Activity": "활동", + "AllowedUploadFormats": "zip 포멧을 통해 플러그인이나 테마를 업로드 할 수 있습니다.", "AuthorHomepage": "작성자 홈페이지", + "Authors": "작성자", + "BackToExtendPiwik": "마켓플레이스로 돌아가", + "BeCarefulUsingPlugins": "Piwik 팀으로부터 검증되지 않은 플러그인은 사용 시 주의해주세요. 해당 플러그인을 리뷰하지 않았습니다.", + "BeCarefulUsingThemes": "Piwik 팀으로부터 검증되지 않은 플러그인은 사용 시 주의해주세요. 해당 플러그인을 리뷰하지 않았습니다.", + "Changelog": "변", "Deactivate": "비활성화", + "Developer": "개발자", + "DoMoreContactPiwikAdmins": "새로운 플러그인이나 테마를 설치하기 위해서, Piwik 관리자에게 연락해주세요.", + "GetEarlyAccessForPaidPlugins": "참고사항: 모든 플러그인은 현재 무료로 사용할 수 있습니다. 향후 유료 마켓플레이스를 제공할 예정입니다 (자세한 사항은 %s연락 주시길%s 바랍니다.).", "Inactive": "비활성", + "InstallNewPlugins": "새로운 플러그인 설치", + "InstallNewThemes": "새로운 테마 설치", + "LastUpdated": "최근 업데이트", "LicenseHomepage": "라이센스 홈페이지", + "LikeThisPlugin": "플러그인 좋아요", + "ConsiderDonating": "기부 고려하기", "PluginsExtendPiwik": "플러그인 확장과 Piwik의 기능성 확장페이지입니다.", "OncePluginIsInstalledYouMayActivateHere": "플러그인이 한번 설치되면 이곳에서 활성화하거나 비활성화할 수 있습니다.", + "Marketplace": "마켓플레이스", + "MenuPlatform": "플랫폼", + "NewVersion": "새로운 버전", + "NoPluginsFound": "플러그인을 찾을 수 없습니다.", + "NoThemesFound": "테마를 찾을 수 없습니다.", + "NoZipFileSelected": "ZIP 파일을 선택해주세요.", + "OriginThirdParty": "서드파티", "PluginHomepage": "플러그인 홈페이지", "PluginKeywords": "검색어", "PluginsManagement": "플러그인 관리", + "PluginWebsite": "플러그인 웹사이트", + "Screenshots": "스크린샷", + "SortByAlpha": "알파버전", + "SortByNewest": "최신", + "SortByPopular": "인기순", "Status": "상태", + "StepDownloadingPluginFromMarketplace": "마켓플레이스에서 플러그인 다운로드 중", + "StepDownloadingThemeFromMarketplace": "마켓플레이스에서 테마 다운로드 중", + "StepUnzippingPlugin": "플러그인 압축 푸는 중", + "StepUnzippingTheme": "테마 압축 푸는 중", + "Theme": "테마", + "Themes": "테마", + "ThemesManagement": "테마 관리", + "UploadZipFile": "ZIP 파일 업로드", "Version": "버전", + "ViewRepositoryChangelog": "변경사항 보기", "Websites": "웹사이트" } }
\ No newline at end of file diff --git a/plugins/CorePluginsAdmin/lang/pt-br.json b/plugins/CorePluginsAdmin/lang/pt-br.json index a068f02cca..479cc31bb8 100644 --- a/plugins/CorePluginsAdmin/lang/pt-br.json +++ b/plugins/CorePluginsAdmin/lang/pt-br.json @@ -8,7 +8,7 @@ "Activated": "Ativado", "Active": "Ativo", "Activity": "Atividade", - "AllowedUploadFormats": "Você pode fazer upload de um plugin ou tema em formato zip através desta página.", + "AllowedUploadFormats": "Você pode carregar um plugin ou tema em formato zip através desta página.", "AuthorHomepage": "Página do autor", "Authors": "Autores", "BackToExtendPiwik": "Voltar ao Marketplace", @@ -89,8 +89,8 @@ "TeaserExtendPiwik": "Estenda Piwik com plugins e temas", "TeaserExtendPiwikByPlugin": "Estenda Piwik instalando um novo plugin", "TeaserExtendPiwikByTheme": "Aproveite um outro look & feel instalando um novo tema", - "TeaserExtendPiwikByUpload": "Estenda Piwik fazendo o upload de um arquivo ZIP", - "InstallingNewPluginViaMarketplaceOrUpload": "Você pode instalar automaticamente plugins do mercado ou %supload um plugin%s em formato .zip.", + "TeaserExtendPiwikByUpload": "Estenda o Piwik carregando um arquivo ZIP", + "InstallingNewPluginViaMarketplaceOrUpload": "Você pode instalar automaticamente plugins do mercado ou %scarregar um plugin%s em formato .zip.", "Theme": "Tema", "Themes": "Temas", "ThemesDescription": "Os temas podem alterar a aparência da interface do usuário do Piwik, e fornecer uma experiência completamente nova visual para desfrutar dos seus relatórios de análise.", @@ -98,7 +98,7 @@ "UninstallConfirm": "Você está prestes a desinstalar um plugin %s. O plugin será completamente removido da sua plataforma e isso não poderá ser desfeito. Tem certeza que deseja fazer isso?", "Updated": "Atualizado(a)", "UpdatingPlugin": "Atualizando %s", - "UploadZipFile": "Enviar arquivo ZIP", + "UploadZipFile": "Carregar arquivo ZIP", "Version": "Versão", "ViewRepositoryChangelog": "Ver as mudanças", "Websites": "Websites" diff --git a/plugins/CorePluginsAdmin/templates/macros.twig b/plugins/CorePluginsAdmin/templates/macros.twig index d7e7c459b0..f0b66123e5 100644 --- a/plugins/CorePluginsAdmin/templates/macros.twig +++ b/plugins/CorePluginsAdmin/templates/macros.twig @@ -184,7 +184,7 @@ 'http://piwik.org', 'http://www.piwik.org', 'http://piwik.org/', 'http://www.piwik.org/' ] %} <span class="plugin-homepage"> - <a href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': ' '})|raw }})</a> + <a target="_blank" href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': ' '})|raw }})</a> </span> {% endif %} diff --git a/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php b/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php index dff84a7fee..bbfd7af081 100644 --- a/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php +++ b/plugins/CorePluginsAdmin/tests/Integration/UpdateCommunicationTest.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\CorePluginsAdmin\tests\Integration; use Piwik\Config; use Piwik\Option; use Piwik\Plugins\CorePluginsAdmin\UpdateCommunication; +use Piwik\Tests\Framework\Fixture; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; /** @@ -121,7 +122,8 @@ class UpdateCommunicationTest extends IntegrationTestCase public function test_sendNotificationIfUpdatesAvailable_ShouldSendCorrectText() { $subject = 'CoreUpdater_NotificationSubjectAvailablePluginUpdate'; - $message = 'ScheduledReports_EmailHello + $rootUrl = Fixture::getTestRootUrl(); + $message = "ScheduledReports_EmailHello CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate @@ -130,9 +132,9 @@ CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate * MyTest3 31.0.0 CoreUpdater_NotificationClickToUpdatePlugins -http://localhost/tests/PHPUnit/proxy/index.php?module=CorePluginsAdmin&action=plugins +{$rootUrl}index.php?module=CorePluginsAdmin&action=plugins -Installation_HappyAnalysing'; +Installation_HappyAnalysing"; $mock = $this->getCommunicationMockHavingManyUpdates(); diff --git a/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php index 156138d770..d43e11ebf4 100644 --- a/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php +++ b/plugins/CoreUpdater/Test/Integration/UpdateCommunicationTest.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\CoreUpdater\Test; use Piwik\Config; use Piwik\Option; use Piwik\Plugins\CoreUpdater\UpdateCommunication; +use Piwik\Tests\Framework\Fixture; use Piwik\UpdateCheck; use Piwik\Version; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; @@ -67,33 +68,35 @@ class UpdateCommunicationTest extends IntegrationTestCase public function test_sendNotifications_shouldSentCorrectEmail() { - $message = 'ScheduledReports_EmailHello + $rootUrl = Fixture::getTestRootUrl(); + $message = "ScheduledReports_EmailHello CoreUpdater_ThereIsNewVersionAvailableForUpdate CoreUpdater_YouCanUpgradeAutomaticallyOrDownloadPackage -http://localhost/tests/PHPUnit/proxy/index.php?module=CoreUpdater&action=newVersionAvailable +{$rootUrl}index.php?module=CoreUpdater&action=newVersionAvailable CoreUpdater_ViewVersionChangelog http://piwik.org/changelog/piwik-33-0-0/ CoreUpdater_FeedbackRequest -http://piwik.org/contact/'; +http://piwik.org/contact/"; $this->assertEmailForVersion('33.0.0', $message); } public function test_sendNotifications_shouldNotIncludeChangelogIfNotMajorVersionUpdate() { - $message = 'ScheduledReports_EmailHello + $rootUrl = Fixture::getTestRootUrl(); + $message = "ScheduledReports_EmailHello CoreUpdater_ThereIsNewVersionAvailableForUpdate CoreUpdater_YouCanUpgradeAutomaticallyOrDownloadPackage -http://localhost/tests/PHPUnit/proxy/index.php?module=CoreUpdater&action=newVersionAvailable +{$rootUrl}index.php?module=CoreUpdater&action=newVersionAvailable CoreUpdater_FeedbackRequest -http://piwik.org/contact/'; +http://piwik.org/contact/"; $this->assertEmailForVersion('33.0.0-b1', $message); } diff --git a/plugins/CoreUpdater/lang/cs.json b/plugins/CoreUpdater/lang/cs.json index 1aba35db9f..d77907655a 100644 --- a/plugins/CoreUpdater/lang/cs.json +++ b/plugins/CoreUpdater/lang/cs.json @@ -19,7 +19,7 @@ "ExceptionArchiveIncompatible": "Nekompatibilní archiv: %s", "ExceptionArchiveIncomplete": "Archiv je nekompletní: některé soubory chybí (např.: %s).", "FeedbackRequest": "Zde můžete s týmem Piwiku sdílet nápady a návrhy:", - "HelpMessageContent": "Zkontrolujte %1$s Piwik FAQ %2$s , kde jsou vysvětleny nejčastější chybi při aktualizaci. %3$s Požádejte vašeho systémového administrátora - může vám pomoct s chybou, která je nejčastěji způsobena nastavením serveru MySQL.", + "HelpMessageContent": "Zkontrolujte %1$s Piwik FAQ %2$s , kde jsou vysvětleny nejčastější chybi při aktualizaci. %3$s Požádejte svéo systémového administrátora - může vám pomoci s chybou, která je nejčastěji způsobena nastavením serveru MySQL.", "HelpMessageIntroductionWhenError": "Níže je hlavní chybová hláška. Pomůže vám zjistit příčinu, ale pokud budete potřebovat další pomoc:", "HelpMessageIntroductionWhenWarning": "Aktualizace proběhla v pořádku, ale byly zaznamenány problémy. Prosím přečtěte si jejich popis výše. Pro další pomoc:", "HighTrafficPiwikServerEnableMaintenance": "Pokud provozujete Piwik server s velkým provozem, doporučujeme %sna chvíli zakázat sledování návštěvníků a přepnout uživatelské rozhraní do režimu údržby%s.", diff --git a/plugins/CoreUpdater/lang/fr.json b/plugins/CoreUpdater/lang/fr.json index 5b170cfe55..4a4e84f983 100644 --- a/plugins/CoreUpdater/lang/fr.json +++ b/plugins/CoreUpdater/lang/fr.json @@ -22,7 +22,7 @@ "HelpMessageContent": "Allez voir la %1$s FAQ Piwik %2$s qui explique les erreurs courantes lors de la mise à jour. %3$s Demandez à votre administrateur système - il pourra être à même avec cette erreur qui est très probablement liée à la configuration de votre serveur MySQL.", "HelpMessageIntroductionWhenError": "Ci-dessus est le message d'erreur du noyau. Cela devrait expliquer la cause, mais si vous nécessitez davantage d'aide merci de :", "HelpMessageIntroductionWhenWarning": "La mise à jour a été complétée avec succès. Cependant, des problèmes sont survenus. Lisez la description ci-dessus pour plus de détails. Pour davantage d'aide :", - "HighTrafficPiwikServerEnableMaintenance": "Si vous gérez un serveur Piwik à haut traffic, nous recommandons de %sdésactiver momentanément le suivit des visiteurs et de mettre l'interface utilisateur de Piwik en mode de maintenance%s.", + "HighTrafficPiwikServerEnableMaintenance": "Si vous gérez un serveur Piwik à haut traffic, nous recommandons de %sdésactiver momentanément le suivi des visiteurs et de mettre l'interface utilisateur de Piwik en mode de maintenance%s.", "IncompatbilePluginsWillBeDisabledInfo": "Note : certains plugins ne sont pas compatbles avec Piwik %s. Ils seront désactivés lorsque vous mettrez à jour :", "InstallingTheLatestVersion": "Installation de la dernière version", "LatestBetaRelease": "Dernière version beta", diff --git a/plugins/CoreUpdater/lang/ja.json b/plugins/CoreUpdater/lang/ja.json index c7f307ea0c..25e1fb5caf 100644 --- a/plugins/CoreUpdater/lang/ja.json +++ b/plugins/CoreUpdater/lang/ja.json @@ -25,6 +25,11 @@ "HighTrafficPiwikServerEnableMaintenance": "高トラフィックの Piwik サーバーを管理する場合、 %smomentarily disable visitor Tracking and put the Piwik User Interface in maintenance mode%s をお勧めします。", "IncompatbilePluginsWillBeDisabledInfo": "注 : 一部のプラグインは、Piwik %s に互換性がありません。アップグレード時に、これらのプラグインは無効になります。", "InstallingTheLatestVersion": "最新バージョンをインストール中", + "LatestBetaRelease": "最新のベータ版 リリース", + "LatestStableRelease": "最新の安定版リリース", + "Latest2XStableRelease": "最新の安定版 2.X", + "Latest2XBetaRelease": "最新のベータ版 2.X", + "LtsSupportVersion": "長期サポート版", "MajorUpdateWarning1": "これは最新版へのアップデートです!通常よりも時間がかかります", "MajorUpdateWarning2": "以下のアドバイスは、特に大規模なインストールの際、重要です。", "NoteForLargePiwikInstances": "Piwik のデータベースが大きい場合の重要注意", @@ -48,11 +53,15 @@ "UpdateHasBeenCancelledExplanation": "Piwik ワンクリックアップデートがキャンセルされました。 あなたが上記のエラーメッセージを修正できない場合は、Piwik を手作業でアップデートすることをお勧めします。%1$s アップデートを始めるには、%2$sアップデートのドキュメント%3$sを参照してください!", "UpdateTitle": "アップデート", "UpdateSuccessTitle": "Piwik は正常にアップグレードされました!", + "UpdateErrorTitle": "アップデートエラー", + "ThankYouUpdatePiwik": "Piwik を利用し、最新の状態を維持して頂きありがとうございます!", + "PostUpdateMessage": "Piwik は、常に無料でダウンロードして自由に使用できますが、成長し、改善するためには皆様のご支援が必要です。", "PostUpdateSupport": "Piwik の使い方で助けが必要な場合は、そのクリエイターからサポートを得ることができます:", "EnterpriseSolutions": "エンタープライズソリューション", "CloudHosting": "クラウドホスティング", "Updating": "アップデート", "UpdateUsingHttpsFailed": "以下のエラーにより、セキュリティで保護された HTTPS 接続で Piwik の最新バージョンをダウンロードできませんでした。", + "UpdateUsingHttpsFailedHelp": "Piwik の最新バージョンのダウンロードが ( セキュリティで保護された HTTPS 接続で ) 失敗する様々な理由として、たとえばネットワーク エラーや低速なネットワーク速度または間違ったシステム構成などがあります。 それはまた、あなたのサーバが MITM 攻撃の標的であり、誰かが Piwik の悪質なバージョンに置き換えて更新しようとしている可能性があることに注意してください。", "UpdateUsingHttpsFailedHelpWhatToDo": "MITM 攻撃を防ぐために、セキュリティで保護された HTTPS 接続を使用してダウンロードを再試行することをお勧めします。", "UsingHttps": "(推奨) セキュリティで保護された HTTPS 接続を使用してください", "UsingHttp": "セキュリティ保護されていない HTTP 接続の使用", diff --git a/plugins/CoreUpdater/lang/ko.json b/plugins/CoreUpdater/lang/ko.json index b54a774bb7..1131e70c6d 100644 --- a/plugins/CoreUpdater/lang/ko.json +++ b/plugins/CoreUpdater/lang/ko.json @@ -21,6 +21,11 @@ "HelpMessageIntroductionWhenError": "위와 같은 경우는 코어 에러 메세지입니다. 원인을 설명할 수 있으나 도움이 필요하다면:", "HelpMessageIntroductionWhenWarning": "업데이트는 성공적으로 완료되었습니다. 하지만 진행중에 문제가 있었습니다. 자세한 사항은 위의 설명을 읽어주세요. 더 많은 도움은:", "InstallingTheLatestVersion": "최근 버전 설치중", + "LatestBetaRelease": "최근 릴리즈 된 베타 버전", + "LatestStableRelease": "최근 릴리즈 된 안정 버전", + "Latest2XStableRelease": "최근 안정 2.X 버전", + "Latest2XBetaRelease": "최근 베타 2.X 버전", + "LtsSupportVersion": "Long Term Support 버전", "MajorUpdateWarning1": "중요한 업데이트입니다! 평소보다 더 오래 걸릴지도 모릅니다.", "MajorUpdateWarning2": "다음은 무거운 설치시 알아야하는 중요한 내용입니다.", "NoteForLargePiwikInstances": "용량이 큰 Piwik 설치시 주의해야 할 점", diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php index 1a41a07df3..5acf9b50be 100644 --- a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php +++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\DataTable; +use Piwik\Metrics; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; use Piwik\View; @@ -40,11 +41,13 @@ class AllColumns extends HtmlTable $this->dataTable->filter(function (DataTable $dataTable) use ($properties) { $columnsToDisplay = array('label', 'nb_visits'); - if (in_array('nb_uniq_visitors', $dataTable->getColumns())) { + $columns = $dataTable->getColumns(); + + if (in_array('nb_uniq_visitors', $columns)) { $columnsToDisplay[] = 'nb_uniq_visitors'; } - if (in_array('nb_users', $dataTable->getColumns())) { + if (in_array('nb_users', $columns)) { $columnsToDisplay[] = 'nb_users'; } diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts -Subproject 6c05ca43d7c83dd795eb713b9feb624d8d6d2eb +Subproject 013821049089ae8d16f67e0a1b6c6525cb88a5a diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions new file mode 160000 +Subproject 5027b250120c8ecccb25d82e50fe99351412298 diff --git a/plugins/CustomVariables/API.php b/plugins/CustomVariables/API.php index fa3a99dbca..69e6ee720c 100644 --- a/plugins/CustomVariables/API.php +++ b/plugins/CustomVariables/API.php @@ -8,10 +8,13 @@ */ namespace Piwik\Plugins\CustomVariables; +use Piwik\API\Request; use Piwik\Archive; +use Piwik\Container\StaticContainer; use Piwik\DataTable; use Piwik\Date; use Piwik\Metrics; +use Piwik\Piwik; use Piwik\Plugins\Actions\Actions\ActionSiteSearch; /** @@ -71,6 +74,7 @@ class API extends \Piwik\Plugin\API } } + if ($flat) { $dataTable->filterSubtables('Piwik\Plugins\CustomVariables\DataTable\Filter\CustomVariablesValuesFromNameId'); } else { @@ -109,9 +113,64 @@ class API extends \Piwik\Plugin\API // Hack Ecommerce product price tracking to display correctly $dataTable->renameColumn('price_viewed', 'price'); } + $dataTable->filter('Piwik\Plugins\CustomVariables\DataTable\Filter\CustomVariablesValuesFromNameId'); return $dataTable; } + + /** + * Get a list of all available custom variable slots (scope + index) and which names have been used so far in + * each slot since the beginning of the website. + * + * @param int $idSite + * @return array + */ + public function getUsagesOfSlots($idSite) + { + Piwik::checkUserHasAdminAccess($idSite); + + $numVars = CustomVariables::getNumUsableCustomVariables(); + + $usedCustomVariables = array( + 'visit' => array_fill(1, $numVars, array()), + 'page' => array_fill(1, $numVars, array()), + ); + + /** @var DataTable $customVarUsages */ + $today = StaticContainer::get('CustomVariables.today'); + $date = '2008-12-12,' . $today; + $customVarUsages = Request::processRequest('CustomVariables.getCustomVariables', + array('idSite' => $idSite, 'period' => 'range', 'date' => $date, + 'format' => 'original') + ); + + foreach ($customVarUsages->getRows() as $row) { + $slots = $row->getMetadata('slots'); + + if (!empty($slots)) { + foreach ($slots as $slot) { + $usedCustomVariables[$slot['scope']][$slot['index']][] = array( + 'name' => $row->getColumn('label'), + 'nb_visits' => $row->getColumn('nb_visits'), + 'nb_actions' => $row->getColumn('nb_actions'), + ); + } + } + } + + $grouped = array(); + foreach ($usedCustomVariables as $scope => $scopes) { + foreach ($scopes as $index => $cvars) { + $grouped[] = array( + 'scope' => $scope, + 'index' => $index, + 'usages' => $cvars + ); + } + } + + return $grouped; + } } diff --git a/plugins/CustomVariables/Archiver.php b/plugins/CustomVariables/Archiver.php index bc43cbc8dd..39aa9e6bb7 100644 --- a/plugins/CustomVariables/Archiver.php +++ b/plugins/CustomVariables/Archiver.php @@ -8,10 +8,10 @@ */ namespace Piwik\Plugins\CustomVariables; -use Piwik\Common; use Piwik\Config; use Piwik\DataAccess\LogAggregator; use Piwik\DataArray; +use Piwik\DataTable; use Piwik\Metrics; use Piwik\Tracker\GoalManager; use Piwik\Tracker; @@ -35,6 +35,9 @@ class Archiver extends \Piwik\Plugin\Archiver protected $maximumRowsInSubDataTable; protected $newEmptyRow; + private $metadata = array(); + private $metadataFlat = array(); + function __construct($processor) { parent::__construct($processor); @@ -50,7 +53,7 @@ class Archiver extends \Piwik\Plugin\Archiver public function aggregateMultipleReports() { - $columnsAggregationOperation = null; + $columnsAggregationOperation = array('slots' => 'uniquearraymerge'); $this->getProcessor()->aggregateDataTableRecords( self::CUSTOM_VARIABLE_RECORD_NAME, @@ -74,6 +77,16 @@ class Archiver extends \Piwik\Plugin\Archiver $this->removeVisitsMetricsFromActionsAggregate(); $this->dataArray->enrichMetricsWithConversions(); $table = $this->dataArray->asDataTable(); + + foreach ($table->getRows() as $row) { + $label = $row->getColumn('label'); + if (!empty($this->metadata[$label])) { + foreach ($this->metadata[$label] as $name => $value) { + $row->addMetadata($name, $value); + } + } + } + $blob = $table->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $columnToSort = Metrics::INDEX_NB_VISITS @@ -118,6 +131,8 @@ class Archiver extends \Piwik\Plugin\Archiver $key = $row[$keyField]; $value = $this->cleanCustomVarValue($row[$valueField]); + $this->addMetadata($keyField, $key, Model::SCOPE_VISIT); + $this->dataArray->sumMetricsVisits($key, $row); $this->dataArray->sumMetricsVisitsPivot($key, $value, $row); } @@ -137,6 +152,8 @@ class Archiver extends \Piwik\Plugin\Archiver $key = $row[$keyField]; $value = $this->cleanCustomVarValue($row[$valueField]); + $this->addMetadata($keyField, $key, Model::SCOPE_PAGE); + $alreadyAggregated = $this->aggregateEcommerceCategories($key, $value, $row); if (!$alreadyAggregated) { $this->aggregateActionByKeyAndValue($key, $value, $row); @@ -145,6 +162,22 @@ class Archiver extends \Piwik\Plugin\Archiver } } + private function addMetadata($keyField, $label, $scope) + { + $index = (int) str_replace('custom_var_k', '', $keyField); + + if (!array_key_exists($label, $this->metadata)) { + $this->metadata[$label] = array('slots' => array()); + } + + $uniqueId = $label . 'scope' . $scope . 'index' . $index; + + if (!isset($this->metadataFlat[$uniqueId])) { + $this->metadata[$label]['slots'][] = array('scope' => $scope, 'index' => $index); + $this->metadataFlat[$uniqueId] = true; + } + } + /** * @param string $key * @param string $value @@ -205,6 +238,7 @@ class Archiver extends \Piwik\Plugin\Archiver } while ($row = $query->fetch()) { $key = $row[$keyField]; + $value = $this->cleanCustomVarValue($row[$valueField]); $this->dataArray->sumMetricsGoals($key, $row); $this->dataArray->sumMetricsGoalsPivot($key, $value, $row); diff --git a/plugins/CustomVariables/Columns/Base.php b/plugins/CustomVariables/Columns/Base.php index 1818ca3145..4c6a4ea542 100644 --- a/plugins/CustomVariables/Columns/Base.php +++ b/plugins/CustomVariables/Columns/Base.php @@ -16,7 +16,7 @@ use Piwik\Plugins\CustomVariables\CustomVariables; class Base extends VisitDimension { - protected function configureSegmentsFor($fieldPrefix, $segmentNameSuffix) + protected function configureSegmentsFor($segmentNameSuffix) { $numCustomVariables = CustomVariables::getNumUsableCustomVariables(); @@ -25,10 +25,7 @@ class Base extends VisitDimension $segment->setSegment('customVariable' . $segmentNameSuffix); $segment->setName($this->getName() . ' (' . Piwik::translate('CustomVariables_ScopeVisit') . ')'); $segment->setCategory('CustomVariables_CustomVariables'); - $segment->setSqlSegment($this->getSegmentColumns('log_visit.' . $fieldPrefix, $numCustomVariables)); - $segment->setSuggestedValuesCallback(function ($idSite, $ignore, DataTable $table) use ($segmentNameSuffix) { - return $table->getColumnsStartingWith('customVariable' . $segmentNameSuffix); - }); + $segment->setUnionOfSegments($this->getSegmentColumns('customVariable' . $segmentNameSuffix, $numCustomVariables)); $this->addSegment($segment); $segment = new Segment(); @@ -36,10 +33,7 @@ class Base extends VisitDimension $segment->setSegment('customVariablePage' . $segmentNameSuffix); $segment->setName($this->getName() . ' (' . Piwik::translate('CustomVariables_ScopePage') . ')'); $segment->setCategory('CustomVariables_CustomVariables'); - $segment->setSqlSegment($this->getSegmentColumns('log_link_visit_action.' . $fieldPrefix, $numCustomVariables)); - $segment->setSuggestedValuesCallback(function ($idSite, $ignore, DataTable $table) use ($segmentNameSuffix) { - return $table->getColumnsStartingWith('customVariablePage' . $segmentNameSuffix); - }); + $segment->setUnionOfSegments($this->getSegmentColumns('customVariablePage' . $segmentNameSuffix, $numCustomVariables)); $this->addSegment($segment); } diff --git a/plugins/CustomVariables/Columns/CustomVariableName.php b/plugins/CustomVariables/Columns/CustomVariableName.php index 85aa46436f..b42f2e8ae7 100644 --- a/plugins/CustomVariables/Columns/CustomVariableName.php +++ b/plugins/CustomVariables/Columns/CustomVariableName.php @@ -14,7 +14,7 @@ class CustomVariableName extends Base { protected function configureSegments() { - $this->configureSegmentsFor('custom_var_k', 'Name'); + $this->configureSegmentsFor('Name'); } public function getName() diff --git a/plugins/CustomVariables/Columns/CustomVariableValue.php b/plugins/CustomVariables/Columns/CustomVariableValue.php index 03046758cf..a565a8136c 100644 --- a/plugins/CustomVariables/Columns/CustomVariableValue.php +++ b/plugins/CustomVariables/Columns/CustomVariableValue.php @@ -14,7 +14,7 @@ class CustomVariableValue extends Base { protected function configureSegments() { - $this->configureSegmentsFor('custom_var_v', 'Value'); + $this->configureSegmentsFor('Value'); } public function getName() diff --git a/plugins/CustomVariables/Controller.php b/plugins/CustomVariables/Controller.php index 59a4a949de..e73e58b257 100644 --- a/plugins/CustomVariables/Controller.php +++ b/plugins/CustomVariables/Controller.php @@ -8,7 +8,20 @@ */ namespace Piwik\Plugins\CustomVariables; +use Piwik\Common; +use Piwik\DataTable; +use Piwik\Piwik; + class Controller extends \Piwik\Plugin\Controller { + public function manage() + { + $idSite = Common::getRequestVar('idSite'); + + Piwik::checkUserHasAdminAccess($idSite); + + return $this->renderTemplate('manage', array()); + } + } diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php index b8a57ad6a6..9059482c33 100644 --- a/plugins/CustomVariables/CustomVariables.php +++ b/plugins/CustomVariables/CustomVariables.php @@ -22,7 +22,10 @@ class CustomVariables extends \Piwik\Plugin { return array( 'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata', - 'Live.getAllVisitorDetails' => 'extendVisitorDetails' + 'Live.getAllVisitorDetails' => 'extendVisitorDetails', + 'AssetManager.getJavaScriptFiles' => 'getJsFiles', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', + 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', ); } @@ -144,4 +147,35 @@ class CustomVariables extends \Piwik\Plugin } } + public function getClientSideTranslationKeys(&$translationKeys) + { + $translationKeys[] = 'CustomVariables_CustomVariables'; + $translationKeys[] = 'CustomVariables_ManageDescription'; + $translationKeys[] = 'CustomVariables_ScopeX'; + $translationKeys[] = 'CustomVariables_Index'; + $translationKeys[] = 'CustomVariables_Usages'; + $translationKeys[] = 'CustomVariables_Unused'; + $translationKeys[] = 'CustomVariables_CreateNewSlot'; + $translationKeys[] = 'CustomVariables_UsageDetails'; + $translationKeys[] = 'CustomVariables_CurrentAvailableCustomVariables'; + $translationKeys[] = 'CustomVariables_ToCreateCustomVarExecute'; + $translationKeys[] = 'CustomVariables_CreatingCustomVariableTakesTime'; + $translationKeys[] = 'CustomVariables_SlotsReportIsGeneratedOverTime'; + $translationKeys[] = 'General_Loading'; + $translationKeys[] = 'General_TrackingScopeVisit'; + $translationKeys[] = 'General_TrackingScopePage'; + } + + public function getStylesheetFiles(&$stylesheets) + { + $stylesheets[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less"; + } + + public function getJsFiles(&$jsFiles) + { + $jsFiles[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js"; + $jsFiles[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js"; + $jsFiles[] = "plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js"; + } + } diff --git a/plugins/CustomVariables/Menu.php b/plugins/CustomVariables/Menu.php new file mode 100644 index 0000000000..b97aa1bb01 --- /dev/null +++ b/plugins/CustomVariables/Menu.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\CustomVariables; + +use Piwik\Common; +use Piwik\Menu\MenuUser; +use Piwik\Piwik; +use Piwik\Plugins\UsersManager\UserPreferences; + +/** + * 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 configureUserMenu(MenuUser $menu) + { + $userPreferences = new UserPreferences(); + $default = $userPreferences->getDefaultWebsiteId(); + $idSite = Common::getRequestVar('idSite', $default, 'int'); + + if (Piwik::isUserHasAdminAccess($idSite)) { + $menu->addManageItem('Custom Variables', $this->urlForAction('manage'), $orderId = 15); + } + } +} diff --git a/plugins/CustomVariables/Model.php b/plugins/CustomVariables/Model.php index 9d98a352fa..235809e1da 100644 --- a/plugins/CustomVariables/Model.php +++ b/plugins/CustomVariables/Model.php @@ -15,12 +15,14 @@ use Piwik\Log; class Model { - const SCOPE_PAGE = 'log_link_visit_action'; - const SCOPE_VISIT = 'log_visit'; - const SCOPE_CONVERSION = 'log_conversion'; const DEFAULT_CUSTOM_VAR_COUNT = 5; + const SCOPE_PAGE = 'page'; + const SCOPE_VISIT = 'visit'; + const SCOPE_CONVERSION = 'conversion'; + private $scope = null; + private $table = null; public function __construct($scope) { @@ -29,21 +31,27 @@ class Model } $this->scope = $scope; + $this->table = Common::prefixTable($this->getTableNameFromScope($scope)); } - public function getScopeName() + private function getTableNameFromScope($scope) { // actually we should have a class for each scope but don't want to overengineer it for now - switch ($this->scope) { + switch ($scope) { case self::SCOPE_PAGE: - return 'Page'; + return 'log_link_visit_action'; case self::SCOPE_VISIT: - return 'Visit'; + return 'log_visit'; case self::SCOPE_CONVERSION: - return 'Conversion'; + return 'log_conversion'; } } + public function getScopeName() + { + return ucfirst($this->scope); + } + /** * @see getHighestCustomVarIndex() * @return int @@ -96,8 +104,7 @@ class Model private function getCustomVarColumnNames() { - $dbTable = $this->getDbTableName(); - $columns = Db::getColumnNamesFromTable($dbTable); + $columns = Db::getColumnNamesFromTable($this->table); $customVarColumns = array_filter($columns, function ($column) { return false !== strpos($column, 'custom_var_'); @@ -108,14 +115,13 @@ class Model public function removeCustomVariable() { - $dbTable = $this->getDbTableName(); - $index = $this->getHighestCustomVarIndex(); + $index = $this->getHighestCustomVarIndex(); if ($index < 1) { return null; } - Db::exec(sprintf('ALTER TABLE %s ', $dbTable) + Db::exec(sprintf('ALTER TABLE %s ', $this->table) . sprintf('DROP COLUMN custom_var_k%d,', $index) . sprintf('DROP COLUMN custom_var_v%d;', $index)); @@ -124,22 +130,16 @@ class Model public function addCustomVariable() { - $dbTable = $this->getDbTableName(); - $index = $this->getHighestCustomVarIndex() + 1; - $maxLen = CustomVariables::getMaxLengthCustomVariables(); + $index = $this->getHighestCustomVarIndex() + 1; + $maxLen = CustomVariables::getMaxLengthCustomVariables(); - Db::exec(sprintf('ALTER TABLE %s ', $dbTable) + Db::exec(sprintf('ALTER TABLE %s ', $this->table) . sprintf('ADD COLUMN custom_var_k%d VARCHAR(%d) DEFAULT NULL,', $index, $maxLen) . sprintf('ADD COLUMN custom_var_v%d VARCHAR(%d) DEFAULT NULL;', $index, $maxLen)); return $index; } - private function getDbTableName() - { - return Common::prefixTable($this->scope); - } - public static function getCustomVariableIndexFromFieldName($fieldName) { $onlyNumber = str_replace(array('custom_var_k', 'custom_var_v'), '', $fieldName); diff --git a/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php b/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php index 44e6d59350..2b068ba03e 100644 --- a/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php +++ b/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php @@ -9,6 +9,8 @@ namespace Piwik\Plugins\CustomVariables\Tracker; use Piwik\Common; +use Piwik\Plugins\CustomVariables\Model; +use Piwik\Tracker\Action; use Piwik\Tracker\Request; use Piwik\Tracker\RequestProcessor; use Piwik\Tracker\Visit\VisitProperties; @@ -36,7 +38,7 @@ class CustomVariablesRequestProcessor extends RequestProcessor public function processRequestParams(VisitProperties $visitProperties, Request $request) { // TODO: re-add optimization where if custom variables exist in request, don't bother selecting them in Visitor - $visitorCustomVariables = $request->getCustomVariables($scope = 'visit'); + $visitorCustomVariables = $request->getCustomVariables($scope = Model::SCOPE_VISIT); if (!empty($visitorCustomVariables)) { Common::printDebug("Visit level Custom Variables: "); Common::printDebug($visitorCustomVariables); @@ -62,4 +64,25 @@ class CustomVariablesRequestProcessor extends RequestProcessor $valuesToUpdate = array_merge($valuesToUpdate, $visitCustomVariables); } } + + public function afterRequestProcessed(VisitProperties $visitProperties, Request $request) + { + $action = $request->getMetadata('Actions', 'action'); + + if (empty($action) || !($action instanceof Action)) { + return; + } + + $customVariables = $action->getCustomVariables(); + + if (!empty($customVariables)) { + Common::printDebug("Page level Custom Variables: "); + Common::printDebug($customVariables); + + foreach ($customVariables as $field => $value) { + $action->setCustomField($field, $value); + } + } + + } } diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js new file mode 100644 index 0000000000..f8098eb114 --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js @@ -0,0 +1,22 @@ +/*! + * 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('ManageCustomVarsController', ManageCustomVarsController); + + ManageCustomVarsController.$inject = ['manageCustomVarsModel', 'piwik', '$filter']; + + function ManageCustomVarsController(manageCustomVarsModel, piwik, $filter) { + manageCustomVarsModel.fetchUsages(); + + this.model = manageCustomVarsModel; + this.siteName = piwik.siteName; + this.scopes = [ + {value: 'visit', name: _pk_translate('General_TrackingScopeVisit')}, + {value: 'page', name: _pk_translate('General_TrackingScopePage')} + ]; + } +})();
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html new file mode 100644 index 0000000000..6ab9ea6ba6 --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html @@ -0,0 +1,54 @@ +<div class="manageCustomVars"> + <h2 piwik-enriched-headline help-url="http://piwik.org/docs/custom-variables/">{{ 'CustomVariables_CustomVariables'|translate }}</h2> + + <p> + <span ng-bind-html="'CustomVariables_ManageDescription'|translate:manageCustomVars.siteName"></span> + </p> + + <div class="alert alert-info" ng-show="!manageCustomVars.model.isLoading && manageCustomVars.model.hasCustomVariablesInGeneral && !manageCustomVars.model.hasAtLeastOneUsage"> + {{ 'CustomVariables_SlotsReportIsGeneratedOverTime'|translate }} + </div> + + <div ng-repeat="scope in manageCustomVars.scopes"> + <h2 class="secondary">{{ 'CustomVariables_ScopeX'|translate:(scope.name) }}</h2> + <table class="dataTable entityTable"> + <thead> + <tr> + <th>{{'CustomVariables_Index'|translate }}</th> + <th>{{'CustomVariables_Usages'|translate }}</th> + </tr> + </thead> + <tbody> + <tr> + <td colspan="3" ng-show="manageCustomVars.model.isLoading">{{ 'General_Loading'|translate }}</td> + </tr> + <tr ng-repeat="customVariables in manageCustomVars.model.customVariables|filter:{scope: scope.value}"> + <td class="index">{{ customVariables.index }}</td> + <td> + <span ng-show="(customVariables.usages|length) === 0" + class="unused">{{'CustomVariables_Unused'|translate }}</span> + <span ng-show="customVariables.usages|length" ng-repeat="cvar in customVariables.usages|orderBy:'-nb_actions'"> + <span title="{{ 'CustomVariables_UsageDetails'|translate:(cvar.nb_visits ? cvar.nb_visits : 0):(cvar.nb_actions ? cvar.nb_actions : 0) }}">{{ cvar.name }}</span><span ng-show="!$last">, </span> + </span> + </td> + </tr> + </tbody> + </table> + </div> + + <h2 class="secondary" ng-show="!manageCustomVars.model.isLoading">{{ 'CustomVariables_CreateNewSlot'|translate }}</h2> + + <p ng-show="!manageCustomVars.model.isLoading"> + {{ 'CustomVariables_CreatingCustomVariableTakesTime'|translate }} + <br /><br /> + <span ng-bind-html="'CustomVariables_CurrentAvailableCustomVariables'|translate:('<strong>'+manageCustomVars.model.numSlotsAvailable+'</strong>')"></span> + <br /> + <br /> + {{ 'CustomVariables_ToCreateCustomVarExecute'|translate }} + <br /> + <br /> + <code>./console customvariables:set-max-custom-variables {{ manageCustomVars.model.numSlotsAvailable + 1 }}</code> + </p> + + +</div>
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js new file mode 100644 index 0000000000..1623d50b27 --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.js @@ -0,0 +1,26 @@ +/*! + * 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-manage-custom-vars> + */ +(function () { + angular.module('piwikApp').directive('piwikManageCustomVars', piwikManageCustomVars); + + piwikManageCustomVars.$inject = ['piwik']; + + function piwikManageCustomVars(piwik){ + return { + restrict: 'A', + scope: {}, + templateUrl: 'plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html?cb=' + piwik.cacheBuster, + controller: 'ManageCustomVarsController', + controllerAs: 'manageCustomVars' + }; + } +})();
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less new file mode 100644 index 0000000000..b666abebca --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.less @@ -0,0 +1,18 @@ +.manageCustomVars { + .unused { + color: @color-silver; + } + + table, p { + width: 900px; + } + + .alert-info { + margin-top: 15px; + } + + .scope, .index { + width: 90px; + max-width: 90px; + } +}
\ No newline at end of file diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js new file mode 100644 index 0000000000..ceb6442a8f --- /dev/null +++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.model.js @@ -0,0 +1,59 @@ +/*! + * 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('manageCustomVarsModel', manageCustomVarsModel); + + manageCustomVarsModel.$inject = ['piwikApi']; + + function manageCustomVarsModel(piwikApi) { + + var model = { + customVariables : [], + extractions : [], + isLoading: false, + fetchUsages: fetchUsages, + hasCustomVariablesInGeneral: false, + hasAtLeastOneUsage: false, + numSlotsAvailable: 5, + }; + + return model; + + function fetchCustomVariables() { + return piwikApi.fetch({method: 'CustomVariables.getCustomVariables', period: 'year', date: 'today', filter_limit: 1}) + .then(function (customVariables) { + model.hasCustomVariablesInGeneral = (customVariables && customVariables.length > 0); + }); + } + + function fetchUsages() { + + model.isLoading = true; + + fetchCustomVariables().then(function () { + return piwikApi.fetch({method: 'CustomVariables.getUsagesOfSlots'}); + + }).then(function (customVariables) { + model.customVariables = customVariables; + + angular.forEach(customVariables, function (customVar) { + if (customVar.index > model.numSlotsAvailable) { + model.numSlotsAvailable = customVar.index; + } + + if (customVar.usages && customVar.usages.length > 0) { + model.hasAtLeastOneUsage = true; + } + }); + + })['finally'](function () { // .finally() is not IE8 compatible see https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c + model.isLoading = false; + }); + } + + } +})();
\ No newline at end of file diff --git a/plugins/CustomVariables/config/config.php b/plugins/CustomVariables/config/config.php index 82a0058e02..cba8dd0f59 100644 --- a/plugins/CustomVariables/config/config.php +++ b/plugins/CustomVariables/config/config.php @@ -1,9 +1,6 @@ <?php return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\CustomVariables\Tracker\CustomVariablesRequestProcessor'), - )), - + // in tests we do not use 'today' to make tests results deterministic + 'CustomVariables.today' => 'today', ); diff --git a/plugins/CustomVariables/config/test.php b/plugins/CustomVariables/config/test.php new file mode 100644 index 0000000000..d1ef28244a --- /dev/null +++ b/plugins/CustomVariables/config/test.php @@ -0,0 +1,4 @@ +<?php +return array( + 'CustomVariables.today' => '2015-01-01' +);
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/cs.json b/plugins/CustomVariables/lang/cs.json index 5a75dd2cd1..98a5db6ea6 100644 --- a/plugins/CustomVariables/lang/cs.json +++ b/plugins/CustomVariables/lang/cs.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Toto hlášení obsahuje informace o vašich vlastních proměnných. Klikněte na proměnnou pro zobrazení distribuce hodnot. %s Pro více informací si přečtěte %sdokumentaci o vlastních proměnných na piwik.org%s", "PluginDescription": "Vlastní proměnné jsou páry (jméno, hodnota), které můžete nastavit návštěvníkovi, nebo libovolné akci, pomocí javascriptového API. Piwik potom ohlásí, kolik návštěv, stránek, konverzí bylo pro každé z těchto jmen a hodnot. Prohlédněte si detailně tyto proměnné pro každého uživatele a akci v záznamu návštěvníků.<br \/>Vyžadováno, pokud chcete použít <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">analýzu E-obchodu<\/a>!", "ScopePage": "rozsah stránky", - "ScopeVisit": "rozsah návštěvy" + "ScopeVisit": "rozsah návštěvy", + "ManageDescription": "Tento přehled zobrazuje všechny sloty vlastních proměnných a jejich využití pro stránku '%s'. Jména v každém ze slotů jsou seřazena dle jejich celkového využití.", + "ScopeX": "Rozsah %s", + "Index": "Index", + "Usages": "Využití", + "Unused": "Nevyužito", + "CreateNewSlot": "Navýšit počet dostupných slotů vlastních proměnných", + "UsageDetails": "%s návštěv a %s akcí od vytvoření této webové stránky.", + "CreatingCustomVariableTakesTime": "Vytvoření nové vlastní proměnné hodnoty může trvat déle v závislosti na velikosti vaší databáze. Proto je toto možné provést pouze pomocí příkazu spuštěnému v příkazové řádce.", + "CurrentAvailableCustomVariables": "Aktuálně můžete použít až %s vlastních proměnných na stránku.", + "ToCreateCustomVarExecute": "Pro vytvoření nového slotu pro vlastní proměnnou spusťte následující příkaz ve své instalaci Piwik:", + "SlotsReportIsGeneratedOverTime": "Data pro tento report budou dostupná později. Než budou data vidět, může to den nebo dva trvat a poté několik týdnů než bude report vypovídající." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/de.json b/plugins/CustomVariables/lang/de.json index 0dd793331a..c60483ec21 100644 --- a/plugins/CustomVariables/lang/de.json +++ b/plugins/CustomVariables/lang/de.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Dieser Bericht enthält Informationen über Ihre benutzerdefinierten Variablen. Klicken Sie auf einen Variablennamen, um die Verteilung der Werte zu sehen. %s Für mehr Informationen über benutzerdefinierte Variablen, lesen Sie die %sDokumentation auf piwik.org%s", "PluginDescription": "Benutzerdefinierte Variablen sind (Namen-, Wert-) Paare, welche Sie mit Hilfe der Javascript API auf Besucher oder deren Aktionen festlegen können. Piwik wird Sie dann über die Menge an Besuchen, Seiten, Konversionen für jede der benutzerdefinierten Variablen und Werte informieren. Die detaillierten benutzerdefinierten Variablen für jeden Benutzer und Aktionen im Besucher-Log einsehbar.<br \/>Benötigt um das Feature <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Ecommerce Analyse<\/a>nutzen zu können.", "ScopePage": "Anwendungsbereich Seite", - "ScopeVisit": "Anwendungsbereich Besuch" + "ScopeVisit": "Anwendungsbereich Besuch", + "ManageDescription": "Diese Übersicht zeigt alle benutzerdefinierten Variablen und deren Gebrauch für Webseite %s. Die Namen auf jeder Position sind danach sortiert, wie oft sie insgesamt benützt wurden.", + "ScopeX": "Anwendungsbereich %s", + "Index": "Index", + "Usages": "Verwendungen", + "Unused": "Unbenutzt", + "CreateNewSlot": "Die Anzahl verfügbarer benutzerdefinierter Variablen erhöhen", + "UsageDetails": "%s Besuche und %s Aktionen seit der Erstellung dieser Webseite.", + "CreatingCustomVariableTakesTime": "Neue benutzerdefinierte Variablen zu erstellen kann viel Zeit beanspruchen, abhängig von der Grösse Ihrer Datenbank. Deshalb ist dieser Vorgang nur über Kommandozeilen verfügbar.", + "CurrentAvailableCustomVariables": "Aktuell können Sie bis zu %s benutzerdefinierte Variablen pro Seite einsetzen.", + "ToCreateCustomVarExecute": "Um eine neue benutzerdefinierte Variable zu erstellen, führen Sie in Ihrer Piwik Installation folgende Befehle aus:", + "SlotsReportIsGeneratedOverTime": "Daten für diesen Bericht werden periodisch veröffentlicht. Es mag ein oder zwei Tage dauern, bis Daten sichtbar sind und einige Wochen bis der Bericht vollständig aussagekräftig ist." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/el.json b/plugins/CustomVariables/lang/el.json index baf9456547..25b790a4f9 100644 --- a/plugins/CustomVariables/lang/el.json +++ b/plugins/CustomVariables/lang/el.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Αυτή η αναφορά περιέχει πληροφορίες για τις Προσαρμοσμένες Μεταβλητές. Πατήστε σε ένα όνομα μεταβλητής για να δείτε την κατανομή των τιμών. %s Για περισσότερες πληροφορίες για τις Προσαρμοσμένες Μεταβλητές γενικά, διαβάστε την %sτεκμηρίωση Προσαρμοσμένων Μεταβλητών στο piwik.org%s.", "PluginDescription": "Οι Προσαρμοσμένες Μεταβλητές (όνομα, τιμή) είναι ζεύγη που δίνετε με χρήση του Javascript API σε επισκέπτες ή οποιαδήποτε ενέργειά τους. Το Piwik τότε θα αναφέρει τις επισκέψεις, σελίδες, μετατροπές για κάθε μία από τις προσαρμοσμένες αυτές μεταβλητές. Δείτε τις λεπτομερείς Προσαρμοσμένες Μεταβλητές για κάθε χρήστη και ενέργεια στο Ημερολόγιο Επισκεπτών.<br \/>Απαιτείται για τη χρήση του χαρακτηριστικού <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Αναλυτικά Ηλ. Εμπορίου<\/a>!", "ScopePage": "σελίδα σκοπού", - "ScopeVisit": "επίσκεψη σκοπού" + "ScopeVisit": "επίσκεψη σκοπού", + "ManageDescription": "Η σύνοψη αυτή δείχνει όλες τις σχισμές προσαρμοσμένων μεταβλητών και τις χρήσεις τους για τον ιστοτόπο '%s'. Τα ονόματα σε κάθε σχισμή ταξινομούνται με βάση το πόσο συχνά χρησιμοποιούνται στο σύνολο.", + "ScopeX": "Εμβέλεια %s", + "Index": "Ευρετήριο", + "Usages": "Χρήσεις", + "Unused": "Δεν χρησιμοποιούνται", + "CreateNewSlot": "Αυξήστε τον αριθμό των σχισμών Προσαρμοσμένων Μεταβλητών", + "UsageDetails": "%s επισκέψεις και %s ενέργειες από την δημιουργία του ιστοτόπου.", + "CreatingCustomVariableTakesTime": "Η δημιουργία σχισμής προσαρμοσμένης μεταβλητής μπορεί να διαρκέσει αρκετή ώρα ανάλογα με το μέγεθος της βάσης. Οπότε, αυτό είναι πρέπει να γίνεται από εντολή που χρειάζεται να εκτελεστεί από την γραμμή εντολών.", + "CurrentAvailableCustomVariables": "Αυτή τη στιγμή μπορείτε να χρησιμοποιήσετε %s Προσαρμοσμένες Μεταβλητές ανά ιστοτόπο.", + "ToCreateCustomVarExecute": "Για να δημιουργήσετε μια σχισμή προσαρμοσμένης μεταβλητής πρέπει να εκτελέσετε την παρακάτω εντολή για την εγκατάσταση του PIwik σας:", + "SlotsReportIsGeneratedOverTime": "Τα δεδομένα για αυτή την αναφορά θα συμπληρώνονται με το πέρασμα του χρόνου. Μπορεί να χρειαστούν μία ή δύο μέρες για να δείτε δεδομένα και μερικές εβδομάδες μέχρι η αναφορά να είναι ακριβής." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/en.json b/plugins/CustomVariables/lang/en.json index 7d99f97bf2..45052df4d9 100644 --- a/plugins/CustomVariables/lang/en.json +++ b/plugins/CustomVariables/lang/en.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "This report contains information about your Custom Variables. Click on a variable name to see the distribution of the values. %s For more information about Custom Variables in general, read the %sCustom Variables documentation on piwik.org%s", "PluginDescription": "Custom Variables are (name, value) pairs that you can assign using the Javascript API to visitors or any of their action. Piwik will then report how many visits, pages, conversions for each of these custom names and values. View the detailed Custom Variables for each user and action in the Visitor Log.<br />Required to use <a href=\"http://piwik.org/docs/ecommerce-analytics/\">Ecommerce Analytics</a> feature!", "ScopePage": "scope page", - "ScopeVisit": "scope visit" + "ScopeVisit": "scope visit", + "ManageDescription": "This overview shows all custom variable slots and their usages for website '%s'. The names within each slot are ordered by how often they were used in total.", + "ScopeX": "Scope %s", + "Index": "Index", + "Usages": "Usages", + "Unused": "Unused", + "CreateNewSlot": "Increase the number of available Custom Variables slots", + "UsageDetails": "%s visits and %s actions since creation of this website.", + "CreatingCustomVariableTakesTime": "Creating a new custom variable slot can take a long time depending on the size of your database. Therefore it is only possible to do this via a command which needs to be executed on the command line.", + "CurrentAvailableCustomVariables": "Currently you can use up to %s Custom Variables per site.", + "ToCreateCustomVarExecute": "To create a new custom variable slot execute the following command within your Piwik installation: ", + "SlotsReportIsGeneratedOverTime": "Data for this report will be populated over time. It may take a day or two to see any data and a few weeks until the report is fully accurate." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/it.json b/plugins/CustomVariables/lang/it.json index 3162fc9027..ec7b07a778 100644 --- a/plugins/CustomVariables/lang/it.json +++ b/plugins/CustomVariables/lang/it.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Questo report contiene le informazioni sulle variabili personalizzate. Clicca su un nome di variabile per visualizzarne la distribuzione dei valori. %s Per ulteriori informazioni sulle variabili personalizzate in generale, leggere la %sDocumentazione Variabili Personalizzatesu piwik.org %s", "PluginDescription": "Le variabili personalizzate sono coppie (nome, valore) che puoi assegnare ai visitatori o a una qualunque loro azione tramite API Javascript. Poi Piwik riporterà le visite, le pagine e le conversioni per questi nomi e valori. Guarda nel dettaglio le Variabili Personalizzate per ciascun utente nel Log Visitatori.<br>Necessario per utilizzare la funzione <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Statistiche Ecommerce<\/a>", "ScopePage": "ambito pagina", - "ScopeVisit": "ambito visita" + "ScopeVisit": "ambito visita", + "ManageDescription": "Questa panoramica mostra tutti gli slot di variabili personali e il loro utilizzo nel sito '%s'. I nomi in ciascuno slot vengono ordinati a seconda di quanto sono stati utilizzati in totale.", + "ScopeX": "Ambito %s", + "Index": "Indice", + "Usages": "Usi", + "Unused": "Inutilizzate", + "CreateNewSlot": "Aumenta il numero degli slot di variabili personali disponibili", + "UsageDetails": "%s visite e %s azioni dalla creazione di questo sito web.", + "CreatingCustomVariableTakesTime": "La creazione di un nuovo slot di variabili personali può richiedere un tempo lungo che dipende dalla dimensione del tuo database. Pertanto è possibile fare ciò solo tramite un comando che deve essere eseguito da riga di comando.", + "CurrentAvailableCustomVariables": "Attualmente puoi utilizzare fino a %s variabili personali per sito.", + "ToCreateCustomVarExecute": "Per creare un nuovo slot di variabili personali esegui il comando seguente nella tua installazione di Piwik.", + "SlotsReportIsGeneratedOverTime": "I dati per questo report saranno popolati nel tempo. Ciò può richiedere un giorno o due per vedere dei dati, e qualche settimana perché il report sia totalmente accurato." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/pt-br.json b/plugins/CustomVariables/lang/pt-br.json index 172cf7eabb..9f46739d4a 100644 --- a/plugins/CustomVariables/lang/pt-br.json +++ b/plugins/CustomVariables/lang/pt-br.json @@ -6,6 +6,17 @@ "CustomVariablesReportDocumentation": "Este relatório contém informações sobre as suas variáveis personalizadas. Clique em um nome de variável para ver a distribuição dos valores. %s para mais informações sobre variáveis personalizadas em geral, leia a documentação %sCustom Variáveis em piwik.org%s", "PluginDescription": "Variáveis Personalizadas são pares (nome, valor) que você pode atribuir utilizando a API Javascript para visitantes ou qualquer ações deles. Então, Piwik reporta o número de visitas, páginas e conversões para cada um desses nomes e valores personalizados. Veja as Variáveis Personalizadas detalhadas para cada usuário e ação no Registro de Visitantes.<br \/> É necessário utilizar a função <a href=\"http:\/\/piwik.org\/docs\/ecommerce-analytics\/\">Ecommerce Analytics<\/a>!", "ScopePage": "página escopo", - "ScopeVisit": "visita escopo" + "ScopeVisit": "visita escopo", + "ManageDescription": "Esta visão geral mostra todos os compartimentos personalizados de variáveis e seus usos para o website '%s'. Os nomes dentro de cada compartimento são ordenados por quantas vezes foram utilizados no total.", + "ScopeX": "Escopo %s", + "Index": "Índice", + "Usages": "Utilizações", + "Unused": "Não utilizado", + "CreateNewSlot": "Aumentar o número de compartimentos de Variáveis Personalizadas disponíveis", + "UsageDetails": "%s visitas e %s ações desde a criação deste website.", + "CreatingCustomVariableTakesTime": "Criar um novo compartimento de variável personalizada pode levar um longo tempo, dependendo do tamanho do seu banco de dados. Por isso, só é possível fazer isso através de um comando que deve ser executado na linha de comando.", + "CurrentAvailableCustomVariables": "Atualmente você pode usar até %s Variáveis Personalizadas por site.", + "ToCreateCustomVarExecute": "Para criar um novo compartimento de variável personalizada execute o seguinte comando dentro da sua instalação Piwik:", + "SlotsReportIsGeneratedOverTime": "Os dados para este relatório serão preenchidos ao longo do tempo. Pode demorar um dia ou dois para ver todos os dados e algumas semanas até que o relatório seja totalmente preciso." } }
\ No newline at end of file diff --git a/plugins/CustomVariables/lang/sk.json b/plugins/CustomVariables/lang/sk.json index f936be8fdd..84445a2338 100644 --- a/plugins/CustomVariables/lang/sk.json +++ b/plugins/CustomVariables/lang/sk.json @@ -1,7 +1,10 @@ { "CustomVariables": { - "ColumnCustomVariableName": "Názov vlastnej premmennej", + "ColumnCustomVariableName": "Názov vlastnej premennej", "ColumnCustomVariableValue": "Hodnota vlastnej premennej", - "CustomVariables": "Vlastné premenné" + "CustomVariables": "Vlastné premenné", + "ScopeX": "Rozsah %s", + "Index": "Index", + "Unused": "Nepoužité" } }
\ No newline at end of file diff --git a/plugins/CustomVariables/templates/manage.twig b/plugins/CustomVariables/templates/manage.twig new file mode 100644 index 0000000000..dc170ff2c9 --- /dev/null +++ b/plugins/CustomVariables/templates/manage.twig @@ -0,0 +1,11 @@ +{% extends 'user.twig' %} + +{% block topcontrols %} + <div class="top_bar_sites_selector piwikTopControl"> + <div piwik-siteselector show-selected-site="true" class="sites_autocomplete"></div> + </div> +{% endblock %} + +{% block content %} + <div piwik-manage-custom-vars> +{% endblock %}
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/Integration/ModelTest.php b/plugins/CustomVariables/tests/Integration/ModelTest.php index 128e5b09ef..905a5f6a1d 100644 --- a/plugins/CustomVariables/tests/Integration/ModelTest.php +++ b/plugins/CustomVariables/tests/Integration/ModelTest.php @@ -19,7 +19,7 @@ use Piwik\Tests\Framework\TestCase\IntegrationTestCase; */ class ModelTest extends IntegrationTestCase { - private static $cvarScopes = array('log_link_visit_action', 'log_visit', 'log_conversion'); + private static $cvarScopes = array('page', 'visit', 'conversion'); public function setUp() { diff --git a/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml b/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml index e504b8e8c7..766be4e97d 100644 --- a/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml +++ b/plugins/CustomVariables/tests/System/expected/test_CustomVariablesSystemTest__CustomVariables.getCustomVariables_day.xml @@ -17,6 +17,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>1</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_1</segment> <subtable> <row> @@ -56,6 +62,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>2</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_2</segment> <subtable> <row> @@ -95,6 +107,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>3</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_3</segment> <subtable> <row> @@ -134,6 +152,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>4</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_4</segment> <subtable> <row> @@ -173,6 +197,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>5</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_5</segment> <subtable> <row> @@ -212,6 +242,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>6</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_6</segment> <subtable> <row> @@ -251,6 +287,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>7</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_7</segment> <subtable> <row> @@ -290,6 +332,12 @@ </goals> <nb_conversions>1</nb_conversions> <revenue>0</revenue> + <slots> + <row> + <scope>visit</scope> + <index>8</index> + </row> + </slots> <segment>customVariableName==Name_VISIT_8</segment> <subtable> <row> @@ -312,100 +360,148 @@ </row> </subtable> </row> - <row> - <label>Name_PAGE_1</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_1</segment> - <subtable> - <row> - <label>Val_PAGE1</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_2</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_2</segment> - <subtable> - <row> - <label>Val_PAGE2</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_3</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_3</segment> - <subtable> - <row> - <label>Val_PAGE3</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_4</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_4</segment> - <subtable> - <row> - <label>Val_PAGE4</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_5</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_5</segment> - <subtable> - <row> - <label>Val_PAGE5</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_6</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_6</segment> - <subtable> - <row> - <label>Val_PAGE6</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_7</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_7</segment> - <subtable> - <row> - <label>Val_PAGE7</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> - <row> - <label>Name_PAGE_8</label> - <nb_actions>1</nb_actions> - <segment>customVariableName==Name_PAGE_8</segment> - <subtable> - <row> - <label>Val_PAGE8</label> - <nb_visits>1</nb_visits> - <nb_actions>1</nb_actions> - </row> - </subtable> - </row> -</result> + <row> + <label>Name_PAGE_1</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>1</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_1</segment> + <subtable> + <row> + <label>Val_PAGE1</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_2</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>2</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_2</segment> + <subtable> + <row> + <label>Val_PAGE2</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_3</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>3</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_3</segment> + <subtable> + <row> + <label>Val_PAGE3</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_4</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>4</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_4</segment> + <subtable> + <row> + <label>Val_PAGE4</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_5</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>5</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_5</segment> + <subtable> + <row> + <label>Val_PAGE5</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_6</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>6</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_6</segment> + <subtable> + <row> + <label>Val_PAGE6</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_7</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>7</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_7</segment> + <subtable> + <row> + <label>Val_PAGE7</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> + <row> + <label>Name_PAGE_8</label> + <nb_actions>1</nb_actions> + <slots> + <row> + <scope>page</scope> + <index>8</index> + </row> + </slots> + <segment>customVariableName==Name_PAGE_8</segment> + <subtable> + <row> + <label>Val_PAGE8</label> + <nb_visits>1</nb_visits> + <nb_actions>1</nb_actions> + </row> + </subtable> + </row> +</result>
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/UI/.gitignore b/plugins/CustomVariables/tests/UI/.gitignore new file mode 100644 index 0000000000..f39be478e7 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/.gitignore @@ -0,0 +1,2 @@ +/processed-ui-screenshots +/screenshot-diffs
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/UI/CustomVariables_spec.js b/plugins/CustomVariables/tests/UI/CustomVariables_spec.js new file mode 100644 index 0000000000..1e247fca56 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/CustomVariables_spec.js @@ -0,0 +1,25 @@ +/*! + * Piwik - free/libre analytics platform + * + * Screenshot integration tests. + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +describe("CustomVariables", function () { + this.timeout(0); + + this.fixture = "Piwik\\Plugins\\CustomVariables\\tests\\Fixtures\\VisitWithManyCustomVariables"; + + it('should show an overview of all used custom variables', function (done) { + expect.screenshot('manage').to.be.captureSelector('.pageWrap', function (page) { + page.load("?idSite=1&period=day&date=2010-01-03&module=CustomVariables&action=manage"); + }, done); + }); + + it('should be visible in the menu', function (done) { + expect.screenshot('link_in_menu').to.be.captureSelector('li:contains(Manage)', function (page) { + }, done); + }); +});
\ No newline at end of file diff --git a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png Binary files differnew file mode 100644 index 0000000000..39480bbf29 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_link_in_menu.png diff --git a/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png Binary files differnew file mode 100644 index 0000000000..c03b7e3795 --- /dev/null +++ b/plugins/CustomVariables/tests/UI/expected-ui-screenshots/CustomVariables_manage.png diff --git a/plugins/DBStats/lang/cs.json b/plugins/DBStats/lang/cs.json index bd3a1b434d..57074b7cdd 100644 --- a/plugins/DBStats/lang/cs.json +++ b/plugins/DBStats/lang/cs.json @@ -6,7 +6,7 @@ "EstimatedSize": "Odhadovaná velikost", "IndexSize": "Velikost indexu", "LearnMore": "Abyste lépe zjistili, jak Piwik zpracovává data a jak jej nastavit pro weby se středním a velkým provozem, podívejte se do dokumentace %s.", - "MainDescription": "Piwik ukládá všechny vaše data webové analýzy v MySQL databázi. Nyní tabulky Piwiku využívají %s.", + "MainDescription": "Piwik ukládá všechna vaše data webové analýzy v MySQL databázi. Nyní tabulky Piwiku využívají %s.", "MetricDataByYear": "Tabulky měření za rok", "MetricTables": "Tabulky měření", "OtherTables": "Ostatní tabulky", diff --git a/plugins/DBStats/lang/ko.json b/plugins/DBStats/lang/ko.json index f56a86418f..8490083722 100644 --- a/plugins/DBStats/lang/ko.json +++ b/plugins/DBStats/lang/ko.json @@ -10,6 +10,7 @@ "MetricDataByYear": "연별 통계 표", "MetricTables": "통계표", "OtherTables": "기타 표", + "PluginDescription": "자세한 MySQL 데이터베이스 사용 보고서를 제공합니다. 진단 상황에서 슈퍼 유저만이 사용할 수 있습니다.", "ReportDataByYear": "연별 보고표", "ReportTables": "보고표", "RowCount": "행 수", diff --git a/plugins/Dashboard/lang/cs.json b/plugins/Dashboard/lang/cs.json index 8db9ceb7cf..1a952809f1 100644 --- a/plugins/Dashboard/lang/cs.json +++ b/plugins/Dashboard/lang/cs.json @@ -13,7 +13,7 @@ "DefaultDashboard": "Výchozí nástěnka - používáte výchozí rozvržení nástěnky", "DeleteWidgetConfirm": "Opravdu chcete odstranit tento widget z nástěnky?", "EmptyDashboard": "Prázdná nástěnka - vyberte si své oblíbené widgety", - "LoadingWidget": "Načítám widget, prosím čekejte...", + "LoadingWidget": "Načítání widgetu, chvíli strpení...", "ManageDashboard": "Správa nástěnky", "Maximise": "Maximalizovat", "Minimise": "Minimalizovat", @@ -21,6 +21,7 @@ "PluginDescription": "Vaše nástěnka analýzy webu. Přizpůsobte si vaši nástěnku přidáním nových widgetů, jejich přesunutím nebo změnou rozvržení sloupců. Každý uživatel může přizpůsobit svou vlastní nástěnku.", "RemoveDashboard": "Odstranit nástěnku", "RemoveDashboardConfirm": "Opravdu chcete odstranit nástěnku: %s?", + "RemoveDefaultDashboardNotPossible": "Výchozí nástěnka nemůže být odstraněna", "RenameDashboard": "Přejmenovat nástěnku", "ResetDashboard": "Obnovit nástěnku", "ResetDashboardConfirm": "Opravdu chcete obnovit nastavení nástěnky do továrního stavu?", diff --git a/plugins/Dashboard/lang/de.json b/plugins/Dashboard/lang/de.json index 6b6a1a6db9..35cc204fec 100644 --- a/plugins/Dashboard/lang/de.json +++ b/plugins/Dashboard/lang/de.json @@ -21,6 +21,7 @@ "PluginDescription": "Ihr Web Analyse Dashboard. Personalisieren Sie Ihr Dashboard, in dem Sie neue Widgets hinzufügen, per Drag und Drop herumschieben, und das Dashboard Spaltenlayout ändern. Jeder Benutzer kann sein eigenes personalisiertes Dashboard verwalten.", "RemoveDashboard": "Dashboard entfernen", "RemoveDashboardConfirm": "Sind Sie sicher, dass Sie das Dashboard \"%s\" löschen möchten?", + "RemoveDefaultDashboardNotPossible": "Das Standard-Dashboard kann nicht entfernt werden.", "RenameDashboard": "Dashboard umbenennen", "ResetDashboard": "Dashboard zurücksetzen", "ResetDashboardConfirm": "Wollen Sie wirklich das Dashboardlayout auf die Standardeinstellungen zurücksetzen?", diff --git a/plugins/Dashboard/lang/el.json b/plugins/Dashboard/lang/el.json index 0f03e52550..965d5329d1 100644 --- a/plugins/Dashboard/lang/el.json +++ b/plugins/Dashboard/lang/el.json @@ -21,6 +21,7 @@ "PluginDescription": "Ο κεντρικός πίνακας Αναλυτικών Ιστού. Προσαρμόστε τον πίνακα με την προσθήκη νέων γραφικών συστατικών, σύρτε και αφήστε τα όπου θέλετε και αλλάξτε την διάταξη στήλης του πίνακα. Κάθε χρήστης μπορεί να διαχειρίζεται τον δικό του πίνακα.", "RemoveDashboard": "Απομάκρυνση κεντρικού πίνακα", "RemoveDashboardConfirm": "Θέλετε, σίγουρα, να απομακρύνετε τον Κεντρικό Πίνακα «%s»;", + "RemoveDefaultDashboardNotPossible": "Ο προκαθορισμένος πίνακας εργαλείων δεν είναι δυνατό να αφαιρεθεί", "RenameDashboard": "Μετονομασία κεντρικού πίνακα", "ResetDashboard": "Επαναφορά κεντρικού πίνακα", "ResetDashboardConfirm": "Θέλετε σίγουρα να επαναφέρετε τον κεντρικό σας πίνακα στις προεπιλεγμένες Μικροεφαρμογές;", diff --git a/plugins/Dashboard/lang/fr.json b/plugins/Dashboard/lang/fr.json index 933c82bf36..1dc3fdb4a1 100644 --- a/plugins/Dashboard/lang/fr.json +++ b/plugins/Dashboard/lang/fr.json @@ -21,6 +21,7 @@ "PluginDescription": "Votre tableau de bord, personnalisez le en ajoutant des widgets, en les déplaçant et en modifiant la disposition des colonnes. Chaque utilisateur possède son propre tableau de bord.", "RemoveDashboard": "Supprimer le tableau de bord", "RemoveDashboardConfirm": "Êtes vous sûr(e) de vouloir supprimer le tableau de bord \"%s\" ?", + "RemoveDefaultDashboardNotPossible": "Le tableau de bord par défaut ne peut pas être supprimé", "RenameDashboard": "Renommer le tableau de bord", "ResetDashboard": "Restaurer le tableau de bord", "ResetDashboardConfirm": "Voulez-vous vraiment restaurer votre tableau de bord avec les widgets par défaut ?", diff --git a/plugins/Dashboard/lang/it.json b/plugins/Dashboard/lang/it.json index b97e35430d..8105f3f90f 100644 --- a/plugins/Dashboard/lang/it.json +++ b/plugins/Dashboard/lang/it.json @@ -21,6 +21,7 @@ "PluginDescription": "La tua Dashboard delle Statistiche Web. Personalizzala aggiungendo nuovi widgets, trascinandoli e spostandoli e cambiando l'aspetto delle colonne. Ciascun utente può gestire una propria dashboard.", "RemoveDashboard": "Rimuovi la dashboard", "RemoveDashboardConfirm": "Sei sicuro di voler rimuovere la dashboard \"%s\"?", + "RemoveDefaultDashboardNotPossible": "La dashboard predefinita non può essere eliminata", "RenameDashboard": "Rinomina dashboard", "ResetDashboard": "Reimposta la dashboard", "ResetDashboardConfirm": "Sei sicuro di voler ripristinare il layout della dashboard alla selezione di widget predefinita?", diff --git a/plugins/Dashboard/lang/ja.json b/plugins/Dashboard/lang/ja.json index 10c0b763d9..f673fd143f 100644 --- a/plugins/Dashboard/lang/ja.json +++ b/plugins/Dashboard/lang/ja.json @@ -21,6 +21,7 @@ "PluginDescription": "ユーザーの Web Analytics のダッシュ ボード。新しいウィジェットを追加することによって、ダッシュ ボードをカスタマイズ、ドラッグ アンドドロップしでダッシュ ボードのカラムのレイアウトを変更します。各ユーザーは、独自のカスタム ダッシュ ボードを管理できます。", "RemoveDashboard": "ダッシュボードの削除", "RemoveDashboardConfirm": "本当にダッシュボード \"%s\" を削除しますか?", + "RemoveDefaultDashboardNotPossible": "既定のダッシュ ボードを削除できません。", "RenameDashboard": "ダッシュボードの名称変更", "ResetDashboard": "ダッシュボードのリセット", "ResetDashboardConfirm": "ダッシュボードのレイアウトをリセットしてデフォルトのウィジェットに戻しますか?", diff --git a/plugins/Dashboard/lang/ko.json b/plugins/Dashboard/lang/ko.json index 535fe10319..6c4dd23b1c 100644 --- a/plugins/Dashboard/lang/ko.json +++ b/plugins/Dashboard/lang/ko.json @@ -21,6 +21,7 @@ "PluginDescription": "당신의 사이트를 분석한 대시보드입니다. 드래그 앤 드롭으로 새로운 위젯을 추가하거나 대시보드 레이아웃을 바꾸는 기능을 통해 당신만의 대시보드를 꾸며보세요. 모든 사용자들은 자신만의 대시보드를 가질 수 있습니다.", "RemoveDashboard": "대시보드 삭제", "RemoveDashboardConfirm": "정말 대시보드 \"%s\"를 삭제 하시겠습니까?", + "RemoveDefaultDashboardNotPossible": "기본 대시보드는 삭제할 수 없습니다.", "RenameDashboard": "대시보드 이름 변경", "ResetDashboard": "대시보드 재설정", "ResetDashboardConfirm": "대시보드의 레이아웃을 재설정하여 기본 위젯으로 복원하시겠습니까?", diff --git a/plugins/Dashboard/lang/pt-br.json b/plugins/Dashboard/lang/pt-br.json index 1ec93d487c..e913ffd254 100644 --- a/plugins/Dashboard/lang/pt-br.json +++ b/plugins/Dashboard/lang/pt-br.json @@ -21,6 +21,7 @@ "PluginDescription": "Seu Painel de Análise da Rede. Personalize seu painel adicionando novos widgets, arrastando para suas posições, e mudando o leiaute das colunas do painel. Cada usuário pode gerenciar seus próprios painéis personalizados.", "RemoveDashboard": "Remover painel", "RemoveDashboardConfirm": "Tem certeza de que deseja remover o painel \"%s\"?", + "RemoveDefaultDashboardNotPossible": "O painel padrão não pode ser removido", "RenameDashboard": "Renomear painel", "ResetDashboard": "Resetar o painel", "ResetDashboardConfirm": "Tem certeza de que deseja redefinir o layout do painel para a seleção Widgets padrão?", diff --git a/plugins/Dashboard/lang/sk.json b/plugins/Dashboard/lang/sk.json index ddd7c84c40..9382122bef 100644 --- a/plugins/Dashboard/lang/sk.json +++ b/plugins/Dashboard/lang/sk.json @@ -21,6 +21,7 @@ "PluginDescription": "Vaša nástenka webovej analýzy. Prispôsobte si svoj panel pridaním nových miniaplikácií, popresúvajte ich, a zmeňte rozvrhnutie stĺpcov panela. Každý používateľ môže spravovať svoju vlastnú nástenku.", "RemoveDashboard": "Odstrániť nástenku", "RemoveDashboardConfirm": "Naozaj chcete odstrániť panel \"%s\"?", + "RemoveDefaultDashboardNotPossible": "Štandardnú nástenku nie je možné odstrániť.", "RenameDashboard": "Premenovať nástenku", "ResetDashboard": "Resetovať nástenku", "ResetDashboardConfirm": "Naozaj chcete resetovať rozvrhnutie vašej nástenky na výber prednastavených miniaplikácií?", diff --git a/plugins/Dashboard/lang/tr.json b/plugins/Dashboard/lang/tr.json index f8ef152082..645b2c2f13 100644 --- a/plugins/Dashboard/lang/tr.json +++ b/plugins/Dashboard/lang/tr.json @@ -19,6 +19,7 @@ "NotUndo": "Bu eylemi geri alamazsınız.", "RemoveDashboard": "Paneli kaldır", "RemoveDashboardConfirm": "\"%s\" panelini kaldırmak istediğinize emin misiniz?", + "RemoveDefaultDashboardNotPossible": "Varsayılan pano silinemez", "RenameDashboard": "Panel yeniden adlandır", "ResetDashboard": "Paneli sıfırla", "ResetDashboardConfirm": "Kontrol paneli şablonunuzu varsayılan widget seçimlerine sıfırlamak istediğinizden emin misiniz?", diff --git a/plugins/DevicePlugins/lang/cs.json b/plugins/DevicePlugins/lang/cs.json index 759e2e487c..3854a6916a 100644 --- a/plugins/DevicePlugins/lang/cs.json +++ b/plugins/DevicePlugins/lang/cs.json @@ -3,7 +3,7 @@ "BrowserWithNoPluginsEnabled": "%1$s bez povolených zásuvných modulů", "BrowserWithPluginsEnabled": "%1$s s povolenými zásuvnými moduly %2$s", "PluginDescription": "Hlásí podporované zásuvné moduly v prohlížečích návštěvníků.", - "PluginDetectionDoesNotWorkInIE": "Poznámka: Detekce zásuvných modulů nepracuje v prohlížeči Interet Explorer. Toto hlášení je založeno na ostatních prohlížečích", + "PluginDetectionDoesNotWorkInIE": "Poznámka: Detekce zásuvných modulů nepracuje v prohlížeči Interet Explorer. Toto hlášení je založeno pouze na jiných prohlížečích", "WidgetPlugins": "Seznam zásuvných modulů", "WidgetPluginsDocumentation": "Toto hlášení zobrazuje zásuvné moduly, které měli vaši návštěvníci povoleny. Tato informace může být důležitá při rozhodování o tom, jakým způsobem prezentovat obsah." } diff --git a/plugins/DevicesDetection/lang/ja.json b/plugins/DevicesDetection/lang/ja.json index 071d79b713..b53d32e622 100644 --- a/plugins/DevicesDetection/lang/ja.json +++ b/plugins/DevicesDetection/lang/ja.json @@ -32,8 +32,10 @@ "PluginDescription": "ブランド ( メーカー ) 、モデル ( デバイス バージョン ) 、デバイスの種類 ( テレビ、コンソール、スマート フォン、デスクトップ ) など、ユーザー デバイスに関する拡張情報を提供します。", "SmartDisplay": "スマートディスプレイ", "Smartphone": "スマートフォン", + "PortableMediaPlayer": "ポータブルメディアプレーヤー", "Devices": "デバイス", "Tablet": "タブレット", + "Phablet": "ファブレット", "TV": "TV", "UserAgent": "ユーザーエージェント", "WidgetBrowsers": "ブラウザ", diff --git a/plugins/DevicesDetection/lang/ko.json b/plugins/DevicesDetection/lang/ko.json index 865556c18b..55711fbbe6 100644 --- a/plugins/DevicesDetection/lang/ko.json +++ b/plugins/DevicesDetection/lang/ko.json @@ -1,15 +1,43 @@ { "DevicesDetection": { - "BrowserEngine": "브라우저", + "BrowserEngine": "브라우저 엔진", "BrowserEngineDocumentation": "이 차트는 방문자의 브라우저를 제품군별로 표시합니다. %s 웹개발자에게 가장 중요한 정보는 사용자의 렌더링 엔진 종류입니다. 엔진의 이름과 괄호 안에 일반적인 해당 브라우저 이름을 표시하고 있습니다.", + "BrowserEngines": "브라우저 엔진들", "BrowserFamily": "브라우저 타입", - "Browsers": "브라우저", + "Browsers": "브라우저들", "BrowserVersion": "브라우저 버전", + "BrowserVersions": "브라우저 버전들", + "Camera": "카메라", + "CarBrowser": "자동차 브라우저", + "Software": "소프트웨어", "ColumnBrowser": "브라우저", - "ColumnOperatingSystem": "운영체제", + "ColumnOperatingSystem": "운영 체제", + "ColumnOperatingSystemVersion": "운영 체제 버전", + "Console": "콘솔", + "dataTableLabelBrands": "브랜드", + "dataTableLabelModels": "모델", + "dataTableLabelSystemVersion": "운영 체제 버전", "dataTableLabelTypes": "유형", + "Device": "기기장치", + "DeviceBrand": "기기장치 브랜드", + "DeviceDetection": "기기장치 탐지", + "DeviceModel": "기기장치 모델", + "DevicesDetection": "방문자 기기장치들", + "DeviceType": "기기장치 타입", + "FeaturePhone": "피처 폰", + "OperatingSystemFamilies": "운영 체제 제품군들", "OperatingSystemFamily": "운영 체제 제품군", "OperatingSystems": "운영체제", + "OperatingSystemVersions": "운영 체제 버전들", + "PluginDescription": "유저의 기기장치에 관하여 좀 더 자세한 정보를 제공합니다. 이는 브랜드(생산자), 모델(기기장치 버전), 타입(텔레비전, 콘솔, 스마트폰, 데스크탑 컴퓨터 등) 외 여러 정보를 포함합니다.", + "SmartDisplay": "스마트 디스플레이", + "Smartphone": "스마트폰", + "PortableMediaPlayer": "휴대용 미디어 플레이어", + "Devices": "기기장치들", + "Tablet": "태블릿", + "Phablet": "패블릿", + "TV": "텔레비전", + "UserAgent": "사용자 에이전트", "WidgetBrowsers": "방문자 브라우저", "WidgetBrowsersDocumentation": "사용자가 어떤 브라우저를 사용하는지에 대한 보고서입니다. 각 브라우저의 버전은 별도로 기재되어 있습니다." } diff --git a/plugins/DevicesDetection/lang/tr.json b/plugins/DevicesDetection/lang/tr.json index 15b3d61a9f..affb773e90 100644 --- a/plugins/DevicesDetection/lang/tr.json +++ b/plugins/DevicesDetection/lang/tr.json @@ -7,8 +7,10 @@ "BrowserVersions": "Tarayıcı versiyonları", "Camera": "Kamera", "CarBrowser": "Araba tarayıcısı", + "Software": "Yazılım", "ColumnBrowser": "Tarayıcı", "ColumnOperatingSystem": "İşletim sistemi", + "ColumnOperatingSystemVersion": "İşletim Sistemi Versiyonu", "Console": "Konsol", "dataTableLabelBrands": "Marka", "dataTableLabelModels": "Model", @@ -21,6 +23,7 @@ "DevicesDetection": "Ziyaretçi Cihazları", "DeviceType": "Cihaz tipi", "OperatingSystemFamilies": "İşletim Sistemi aileleri", + "OperatingSystemFamily": "İşletim Sistemi Ailesi", "OperatingSystems": "İşletim sistemleri", "OperatingSystemVersions": "İşletim Sistemi versiyonları", "SmartDisplay": "Akıllı görüntüleme", diff --git a/plugins/Ecommerce/config/config.php b/plugins/Ecommerce/config/config.php deleted file mode 100644 index 3431748e41..0000000000 --- a/plugins/Ecommerce/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Ecommerce\Tracker\EcommerceRequestProcessor'), - )), - -); diff --git a/plugins/Ecommerce/lang/ja.json b/plugins/Ecommerce/lang/ja.json index 62ccf9fe7d..822234b795 100644 --- a/plugins/Ecommerce/lang/ja.json +++ b/plugins/Ecommerce/lang/ja.json @@ -1,5 +1,8 @@ { "Ecommerce": { - "Sales": "売り上げ" + "PluginDescription": "E コマースでは、いつユーザーがカートに製品を追加し、いつ E コマース販売に結びついたか、を追跡できます。製品、製品のカテゴリビュー、および放棄されたカートも追跡できます。", + "Sales": "売り上げ", + "SalesBy": "%s 別売り上げ高", + "SalesAdjective": "売り上げ %s" } }
\ No newline at end of file diff --git a/plugins/Events/Actions/ActionEvent.php b/plugins/Events/Actions/ActionEvent.php index 9921c8d39b..5106ec6092 100644 --- a/plugins/Events/Actions/ActionEvent.php +++ b/plugins/Events/Actions/ActionEvent.php @@ -60,9 +60,17 @@ class ActionEvent extends Action protected function getActionsToLookup() { - return array( - 'idaction_url' => $this->getUrlAndType() - ); + $actionUrl = false; + + $url = $this->getActionUrl(); + + if (!empty($url)) { + // normalize urls by stripping protocol and www + $url = Tracker\PageUrl::normalizeUrl($url); + $actionUrl = array($url['url'], $this->getActionType(), $url['prefixId']); + } + + return array('idaction_url' => $actionUrl); } // Do not track this Event URL as Entry/Exit Page URL (leave the existing entry/exit) diff --git a/plugins/Events/lang/ja.json b/plugins/Events/lang/ja.json index 4304953ddf..10051096aa 100644 --- a/plugins/Events/lang/ja.json +++ b/plugins/Events/lang/ja.json @@ -1,5 +1,6 @@ { "Events": { + "PluginDescription": "イベントを追跡し、ビジターアクティビティのレポートを取得します。", "AvgEventValue": "イベントの平均値: %s", "AvgValue": "平均値", "AvgValueDocumentation": "このイベントのすべての値の平均値", @@ -14,6 +15,7 @@ "EventsWithValue": "値を持つイベント", "EventsWithValueDocumentation": "イベントの値が設定されたイベント数", "EventValue": "イベントの値", + "EventValueTooltip": "総イベント値は %s 件のイベント値の合計です。 %s 最小値は %s 、最大値は %s です 。", "MaxValue": "最大値", "MaxValueDocumentation": "このイベントの最大値", "MinValue": "最小値", diff --git a/plugins/Events/lang/sk.json b/plugins/Events/lang/sk.json index 396cf2c319..a45b722eb4 100644 --- a/plugins/Events/lang/sk.json +++ b/plugins/Events/lang/sk.json @@ -1,5 +1,14 @@ { "Events": { - "AvgValue": "Priemerná hodnota" + "PluginDescription": "Sledovanie Udalostí a získanie reportu ohľadom aktivity vašich návštevníkov.", + "AvgValue": "Priemerná hodnota", + "Events": "Udalosti", + "EventsWithValue": "Udalosti s hodnotou", + "EventsWithValueDocumentation": "Počet udalostí, kde bola nastavená hodnota Udalosti", + "EventValueTooltip": "Hodnota Udalostí celom je suma %s hodnôt udalostí %s medzi minimom %s a maximom %s.", + "TopEvents": "Top Udalosti", + "TotalEvents": "Udalosti celkom", + "TotalEventsDocumentation": "Celkový počet udalostí", + "ViewEvents": "Zobraziť udalosti" } }
\ No newline at end of file diff --git a/plugins/ExamplePlugin/tests/travis/addons.apt.packages.yml b/plugins/ExamplePlugin/tests/travis/addons.apt.packages.yml new file mode 100644 index 0000000000..db3733500a --- /dev/null +++ b/plugins/ExamplePlugin/tests/travis/addons.apt.packages.yml @@ -0,0 +1,3 @@ +- custom apt package +- another custom apt package + diff --git a/plugins/ExamplePlugin/tests/travis/addons.apt.sources.yml b/plugins/ExamplePlugin/tests/travis/addons.apt.sources.yml new file mode 100644 index 0000000000..3504520e3c --- /dev/null +++ b/plugins/ExamplePlugin/tests/travis/addons.apt.sources.yml @@ -0,0 +1 @@ +- custom apt source
\ No newline at end of file diff --git a/plugins/Feedback/lang/cs.json b/plugins/Feedback/lang/cs.json index 42d124d0a4..935e9913b4 100644 --- a/plugins/Feedback/lang/cs.json +++ b/plugins/Feedback/lang/cs.json @@ -5,11 +5,11 @@ "IWantTo": "Chci:", "LearnWaysToParticipate": "Naučit způsoby jak %s spolupracovat %s", "ManuallySendEmailTo": "Prosím pošlete ručně zprávu", - "PluginDescription": "Pošlete tým Piwiku vaši odezvu. Podělte se o své návrhy a nápady a pomozte nám, aby se Piwi stal nejlepší analytickou platformou vůbec.", + "PluginDescription": "Pošlete tým Piwiku vaši odezvu. Podělte se o své návrhy a nápady a pomozte nám, aby se Piwik stal nejlepší analytickou platformou vůbec.", "PrivacyClaim": "Piwik respektuje vaše %1$ssoukromí%2$s a dává vám plnou kontrolu nad vašimi daty.", "RateFeatureLeaveMessageDislike": "Je nám líto, že se vám to nelíbí. Řekněte nám, jak se můžeme zlepšit.", "RateFeatureLeaveMessageLike": "Jsme rádi, že se vám to líbí. Řekněte nám, co se vám líbí nejvíc, nebo jestli máte nějaký návrh na novou funkci.", - "RateFeatureSendFeedbackInformation": "Piwik platforma nám (týmu Piwiku) pošle e-mail s vaší e-mailovou adresou, abychom vás v případě otázek mohli kontaktovat.", + "RateFeatureSendFeedbackInformation": "Piwik platforma nám (týmu Piwiku) pošle zprávu s vaší emailovou adresou, abychom vás v případě otázek mohli kontaktovat.", "RateFeatureThankYouTitle": "Děkujeme za ohodnocení %s.", "RateFeatureTitle": "Líbí se vám vlastnost %s? Ohodnoťte ji a zanechte komentář", "SendFeedback": "Odeslat odezvu", @@ -22,8 +22,8 @@ "PiwikProIntro": "Piwik Pro poskytuje konzultace s experty pro ty, kteří hostují Piwik na vlastní infrastruktuře.", "PiwikProOfferIntro": "Naše nabídka obsahuje", "PiwikProReviewPiwikSetup": "Kontrolu vašeho nastavení Piwik", - "PiwikProOptimizationMaintenance": "Služby optimalizace & údržby Piwiku", - "PiwikProPhoneEmailSupport": "Telefonická a e-mailová podpora", + "PiwikProOptimizationMaintenance": "Služby optimalizace a údržby Piwiku", + "PiwikProPhoneEmailSupport": "Telefonická a emailová podpora", "PiwikProTraining": "Uživatelská, technická a vývojářská školení", "PiwikProPremiumFeatures": "Prémiové funkce", "PiwikProCustomDevelopment": "Služby vlastního vývoje", diff --git a/plugins/Feedback/lang/fr.json b/plugins/Feedback/lang/fr.json index 9bfff5b40f..b992c5cfbc 100644 --- a/plugins/Feedback/lang/fr.json +++ b/plugins/Feedback/lang/fr.json @@ -1,7 +1,7 @@ { "Feedback": { "DoYouHaveBugReportOrFeatureRequest": "Avez-vous un bug à rapporter ou une fonctionnalité à demander ?", - "HowToCreateTicket": "Veuillez consulter les recommandations sur la rédaction d'un bon %1$srapport de bug%2$s ou %3$sdemande de fonctionnalité%4$s. Puis s'enregistrer ou se connecter sur %5$snotre système de suivit des incidents%6$s et créer un %7$snouvel incident%8$s.", + "HowToCreateTicket": "Veuillez consulter les recommandations sur la rédaction d'un bon %1$srapport de bug%2$s ou %3$sdemande de fonctionnalité%4$s. Puis s'enregistrer ou se connecter sur %5$snotre système de suivi des incidents%6$s et créer un %7$snouvel incident%8$s.", "IWantTo": "Je veux:", "LearnWaysToParticipate": "Renseignez vous sur les manières dont vous pouvez %s participer%s", "ManuallySendEmailTo": "Merci d'envoyer manuellement votre message à", diff --git a/plugins/Feedback/lang/ja.json b/plugins/Feedback/lang/ja.json index c66634eb43..1819573a93 100644 --- a/plugins/Feedback/lang/ja.json +++ b/plugins/Feedback/lang/ja.json @@ -17,12 +17,14 @@ "TopLinkTooltip": "あなたの意見をお知らせください。または、プロフェッショナルサポートをリクエストしてください。", "ViewAnswersToFAQ": "%s Frequently Asked Questions %s への回答をご覧ください。", "ViewUserGuides": "Piwik の設定方法と、効果的なデータ分析方法は、%1$s user guides %2$s をご確認ください。", + "CommunityHelp": "コミュニティヘルプ", "ProfessionalHelp": "プロフェッショナルヘルプ", "PiwikProIntro": "Piwik PRO は、専門家のサポートと Piwik 独自のインフラストラクチャ上でホストするクライアントへのコンサルティングを提供します。", "PiwikProOfferIntro": "当社オファーには以下が含まれます", "PiwikProReviewPiwikSetup": "Piwik セットアップのレビュー", "PiwikProOptimizationMaintenance": "Piwikiの最適化と保守のサービス", "PiwikProPhoneEmailSupport": "電話とEメールのサポート", + "PiwikProTraining": "ユーザー及び技術開発者のトレーニング", "PiwikProPremiumFeatures": "プレミア機能", "PiwikProCustomDevelopment": "カスタム開発サービス", "PiwikProAnalystConsulting": "アナリストのコンサルティングサービス", diff --git a/plugins/Feedback/lang/ko.json b/plugins/Feedback/lang/ko.json index ed80f5b83b..64a1086e78 100644 --- a/plugins/Feedback/lang/ko.json +++ b/plugins/Feedback/lang/ko.json @@ -1,12 +1,34 @@ { "Feedback": { "DoYouHaveBugReportOrFeatureRequest": "발견한 버그나 기능에 대한 의견을 보고하시겠습니까?", + "HowToCreateTicket": "좋은 %1$s버그 리포트%2$s 혹은 %3$s기능 개선%4$s을 작성하는 추천법에 대해 읽어주세요. 그 후 %5$s우리의 이슈 트래커%6$s에 가입 및 로그인 하셔서 %7$s새로운 이슈%8$s를 만들어주세요.", "IWantTo": "내가 원하는 것은:", "LearnWaysToParticipate": "당신이 %s참여%s할 수있는 모든 방법", "ManuallySendEmailTo": "당신의 메시지를 다음 주소로 직접 보내주세요:", + "PluginDescription": "Piwik 팀에게 피드백 보내기. 당신의 아이디어와 제안은 더 나은 세상에서 가장 우수한 분석 플랫폼이 될 Piwik를 만드는데 큰 도움이 됩니다.", + "PrivacyClaim": "Piwik는 당신의 %1$s프라이버시%2$s를 존중하며 동시에 당신의 데이터가 당신의 통제 하에 있도록 노력합니다.", + "RateFeatureLeaveMessageDislike": "안 좋아하신다고요! 죄송합니다! 우리가 이를 개선할 수 있도록 도움을 주세요!", + "RateFeatureLeaveMessageLike": "좋아요에 감사 드립니다! 어떤 점을 좋게 생각하시는지 혹은 어떤 기능적 개선 요구가 있는지 알고 싶습니다.", + "RateFeatureSendFeedbackInformation": "당신의 Piwik 플랫폼은 우리들(Piwik 팀)에게 (당신의 이메일 주소를 포함한) 메일을 보낼 것입니다. 따라서 당신의 질문에 따라 우리가 연락을 취할 수 있습니다.", + "RateFeatureThankYouTitle": "'%s' 등급에 감사 드립니다.", + "RateFeatureTitle": "'%s'의 기능이 어떤가요? 평가 및 코멘트 해주세요.", "SendFeedback": "의견 보내기", "ThankYou": "우리가 Piwik을 향상시키는 데 도움을 주셔서 감사합니다!", - "TopLinkTooltip": "당신이 생각하는 기술 지원 요청을 알려주세요.", + "TopLinkTooltip": "당신이 필요로 하는 기술 지원 요청을 알려주세요.", + "ViewAnswersToFAQ": "%s자주 하는 질문%s에서 답변 보기", + "ViewUserGuides": "%1$s사용자 가이드%2$s에서 효율적으로 당신의 데이터를 분석하는 방법과 Piwik를 구성하는 법에 대해서 알 수 있습니다.", + "CommunityHelp": "커뮤니티에서의 도움", + "ProfessionalHelp": "전문가의 도움", + "PiwikProIntro": "Piwik PRO는 전문가들이 구축한 인프라에서 그들의 지원과 컨설팅을 받을 수 있는 서비스입니다.", + "PiwikProOfferIntro": "제공되는 것들", + "PiwikProReviewPiwikSetup": "Piwik 셋업", + "PiwikProOptimizationMaintenance": "Piwik 최적화 및 관리 보수", + "PiwikProPhoneEmailSupport": "전화 및 이메일", + "PiwikProTraining": "사용자, 기술자 및 개발자", + "PiwikProPremiumFeatures": "프리미엄", + "PiwikProCustomDevelopment": "맞춤 개발", + "PiwikProAnalystConsulting": "전문가 컨설팅", + "ContactUs": "연락주세요", "VisitTheForums": "%s포럼%s으로 이동" } }
\ No newline at end of file diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php index 351e700782..89af85f9c0 100644 --- a/plugins/Goals/API.php +++ b/plugins/Goals/API.php @@ -366,7 +366,6 @@ class API extends \Piwik\Plugin\API 'date' => $date, 'idGoal' => $idGoal, 'columns' => $columns, - 'serialize' => '0', 'format_metrics' => 'bc' )); diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php index 9ab7a96d66..571a5f75cd 100644 --- a/plugins/Goals/Pages.php +++ b/plugins/Goals/Pages.php @@ -292,8 +292,14 @@ class Pages $report['viewDataTable'] = 'tableGoals'; } + if (!empty($report['parameters'])) { + $params = array_merge($customParams, $report['parameters']); + } else { + $params = $customParams; + } + $widget = $this->createWidgetForReport($report['module'], $report['action']); - $widget->setParameters($customParams); + $widget->setParameters($params); $widget->setCategoryId($categoryText); $widget->setSubcategoryId($categoryText); $widget->setOrder($order); diff --git a/plugins/Goals/Reports/Base.php b/plugins/Goals/Reports/Base.php index 3f9cb9c98a..f9e36d6787 100644 --- a/plugins/Goals/Reports/Base.php +++ b/plugins/Goals/Reports/Base.php @@ -33,7 +33,7 @@ abstract class Base extends \Piwik\Plugin\Report $this->parameters = array('idGoal' => $goal['idgoal']); $this->order = $this->orderGoal + $goal['idgoal'] * 3; - $availableReports[] = $this->buildReportMetadata($availableReports, $infos); + $availableReports[] = $this->buildReportMetadata(); } $this->init(); diff --git a/plugins/Goals/config/config.php b/plugins/Goals/config/config.php deleted file mode 100644 index 1199467965..0000000000 --- a/plugins/Goals/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Goals\Tracker\GoalsRequestProcessor'), - )), - -); diff --git a/plugins/Goals/lang/cs.json b/plugins/Goals/lang/cs.json index 7bc7cbc8bb..e229f04e14 100644 --- a/plugins/Goals/lang/cs.json +++ b/plugins/Goals/lang/cs.json @@ -9,15 +9,15 @@ "BestCountries": "Země s nejvyšším počtem konverzí jsou:", "BestKeywords": "Klíčová slova s nejvyšším počtem konverzí jsou:", "BestReferrers": "Odkazující stránky s nejvyšším počtem konverzí jsou:", - "CaseSensitive": "shoda s velikostí písmen", + "CaseSensitive": "rozlišovat velikost písmen", "CancelAndReturnToGoals": "Zrušit a %svrátit se na seznam cílů%s", "CategoryTextGeneral_Visitors": "Umístění uživatele", "CategoryTextReferrers_Referrers": "Referrery", "CategoryTextVisitsSummary_VisitsSummary": "Uživatelský atribut", "CategoryTextGeneral_Visit": "Zapojení", "ChooseGoal": "Vyberte cíl", - "ClickOutlink": "Kliknout na odkaz na externí web", - "SendEvent": "Poslat událost", + "ClickOutlink": "Kliknou na odkaz na externí web", + "SendEvent": "Odešlou událost", "ColumnAverageOrderRevenueDocumentation": "Průměrná hodnota objednávky (AOV) je celkový příjem ze všech objednávek dělený jejich počtem.", "ColumnAveragePriceDocumentation": "Průměrný příjem z tohoto %s.", "ColumnAverageQuantityDocumentation": "Průměrný počet tohoto %s prodaný v elektronických objednávkách.", @@ -45,7 +45,7 @@ "DefaultRevenueHelp": "Na příklad kontaktní formulář odeslaný návštěvníkem má průměrnou cenu $10. Piwik vám pomůže dobře pochopit chování skupin uživatelů", "DeleteGoalConfirm": "Jste si jisti, že chcete vymazat tento cíl %s?", "DocumentationRevenueGeneratedByProductSales": "Tržby produktu. Nepočítají se daně, poplatky za doručení a slevy.", - "Download": "Stáhnout soubor", + "Download": "Stáhnou soubor", "Ecommerce": "Obchody", "EcommerceAndGoalsMenu": "Obchody a Cíle", "EcommerceLog": "Logy", @@ -58,17 +58,17 @@ "GoalConversion": "Cíl konverze", "GoalConversions": "Konverze cíle", "GoalConversionsBy": "Konverze cíle %s podle typu návštěvy", - "GoalIsTriggered": "Cíle je zaznamenáván", - "GoalIsTriggeredWhen": "Cíl je zaznamenáván, když", + "GoalIsTriggered": "Cíl je zaznamenán", + "GoalIsTriggeredWhen": "Cíl je zaznamenán, pokud", "GoalName": "Jméno cíle", "Goals": "Cíle", "ManageGoals": "Spravovat cíle", "GoalsOverview": "Přehled cílů", - "GoalsOverviewDocumentation": "Toto je přehled vašich konverzí cílů. VNe vchozím stavu graf zobrazuje součet všech konverzí. %s Pod grafem jsou zobrazena hlášení pro každý cíl. Můžete je zvětšit kliknutím, pokud chcete.", + "GoalsOverviewDocumentation": "Toto je přehled vašich konverzí cílů. Ve výchozím stavu graf zobrazuje součet všech konverzí. %s Pod grafem jsou zobrazena hlášení pro každý cíl. Můžete je zvětšit kliknutím, pokud chcete.", "GoalX": "Cíl: %s", "HelpOneConversionPerVisit": "Pokud byla stránka odpovídající cíli při návštěvě obnovena nebo zobrazena vícekrát, bude cíl započítán pouze jednou a to při prvním zobrazení.", "IsExactly": "je přesně %s", - "LearnMoreAboutGoalTrackingDocumentation": "Více o sledování cílu se dozvíte v %suživatelské dokumentaci%s.", + "LearnMoreAboutGoalTrackingDocumentation": "Více o sledování cílů se dozvíte v %suživatelské dokumentaci%s.", "LeftInCart": "%s Zbylo v košíku", "ManageGoalsOrCreateANewGoal": "%sSpravovat cíle%s nebo ho vytvořte.", "Manually": "ručně", @@ -86,7 +86,7 @@ "PluginDescription": "Vytvořte cíle a sledujte detailní hlášení o jejich konverzích: vývoj v čase, příjem za návštěvu, konverze prro referrer, pro klíčové slovo a více.", "ProductCategory": "Kategorie produktu", "ProductName": "Název produktu", - "Products": "Produktů", + "Products": "Produkty", "ProductSKU": "SKU produktu", "ReturningVisitorsConversionRateIs": "Poměr konverze navracejících se uživatelů je %s", "SingleGoalOverviewDocumentation": "Toto je přehled konverzí jednoho cíle. %s Linky lze zvětšit kliknutím na ně.", @@ -96,12 +96,12 @@ "ViewAndEditGoals": "Zobrazit a editovat cíle", "GoalsBy": "Cíle podle %s", "GoalsAdjective": "Cíle %s", - "VisitPageTitle": "Navštívit stránku s daným titulkem", + "VisitPageTitle": "Navštíví stránku s daným titulkem", "VisitsUntilConv": "Návštěv ke konverzi", - "VisitUrl": "Navštívít zadanou URL (stránku, nebo skupiny stránek)", - "WhenVisitors": "když návštěvníci", + "VisitUrl": "Navštíví zadanou URL (stránku nebo skupiny stránek)", + "WhenVisitors": "pokud návštěvníci", "WhereThe": "když", - "WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore": "kde návštívená stránka obsahuhe volání metody JavaScriptu piwikTracker.trackGoal() (%svíce%s)", - "YouCanEnableEcommerceReports": "Pro tyto stránky můžete %s povolit na stránce %s." + "WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore": "kde navštívená stránka obsahuhe volání metody JavaScriptu 'trackGoal' (%svíce%s)", + "YouCanEnableEcommerceReports": "Pro tyto stránky můžete povolit %s na stránce %s." } }
\ No newline at end of file diff --git a/plugins/Goals/lang/fr.json b/plugins/Goals/lang/fr.json index c8945cf794..8518515c2e 100644 --- a/plugins/Goals/lang/fr.json +++ b/plugins/Goals/lang/fr.json @@ -76,7 +76,7 @@ "MatchesExpression": "correspond à l'expression %s", "NewGoalIntro": "Le suivi de la conversion des objectifs et une des manières les plus efficaces de mesurer et améliorer vos objectifs d'affaires.", "NewVisitorsConversionRateIs": "Le taux de conversion des nouveaux visiteurs est %s", - "NoGoalsNeedAccess": "Uniquement un Administrateur ou un utilisateur avec un accès Super Utilisateur peut gérer les Objectifs d'un site web donné. Veuillez demander à votre administrateur Piwik de mettre en place un Objectif pour votre site web. <br \/>Le suivit d'Objectifs est une bonne manière de mieux comprendre et de maximiser les performances de votre site web!", + "NoGoalsNeedAccess": "Uniquement un Administrateur ou un utilisateur avec un accès Super Utilisateur peut gérer les Objectifs d'un site web donné. Veuillez demander à votre administrateur Piwik de mettre en place un Objectif pour votre site web. <br \/>Le suivi d'objectifs est une bonne manière de mieux comprendre et de maximiser les performances de votre site web!", "NeedAccess": "Uniquement un administrateur ou un utilisateur avec le rôle super utilisateur peut gérer les objectifs pour un site web.", "Optional": "(optionnel)", "OverallConversionRate": "%s taux de conversion global (visites avec un objectif rempli)", diff --git a/plugins/Goals/lang/ja.json b/plugins/Goals/lang/ja.json index 9699895d82..bbb26f8859 100644 --- a/plugins/Goals/lang/ja.json +++ b/plugins/Goals/lang/ja.json @@ -11,6 +11,10 @@ "BestReferrers": "ベストコンバージョンの参照元ウェブサイト", "CaseSensitive": "大文字小文字を区別する一致", "CancelAndReturnToGoals": "キャンセルして目標 %s のリスト %s に戻る", + "CategoryTextGeneral_Visitors": "ユーザーの位置情報", + "CategoryTextReferrers_Referrers": "リファラー", + "CategoryTextVisitsSummary_VisitsSummary": "ユーザー属性", + "CategoryTextGeneral_Visit": "エンゲージメント", "ChooseGoal": "Choose Goal", "ClickOutlink": "外部ウェブサイトへのリンクをクリック", "SendEvent": "イベントを送信", @@ -35,6 +39,7 @@ "ConversionsOverview": "コンバージョンの概観", "ConversionsOverviewBy": "ビジットの種類によるコンバージョン概観", "DaysToConv": "日数別のコンバージョン", + "Details": "目標の詳細", "DefaultGoalConvertedOncePerVisit": "(デフォルト)目標達成は訪問の都度1回だけカウント", "DefaultRevenue": "目標のデフォルト収益", "DefaultRevenueHelp": "例えば、ビジターによって送信されたコンタクトフォームが、平均して $10 の価値を持つかもしれません。 Piwik は、ビジターセグメントがどれくらいうまく実行しているかを理解するのを助けます。", @@ -71,6 +76,8 @@ "MatchesExpression": "正規表現 %s に一致する", "NewGoalIntro": "目標コンバージョントラッキングは、あなたのビジネス目標の測定や改善のために、最も効率的な方法の一つです。", "NewVisitorsConversionRateIs": "新規ビジターのコンバージョン率は %s です", + "NoGoalsNeedAccess": "管理者またはスーパー ユーザーのアクセス権を持つユーザーだけが、ウェブサイトの目標を管理できます。ウェブサイトの目標を設定する場合は Piwik の管理者に依頼してください。<br> 目標を追跡することは、ウェブサイトの理解に役立ち、パフォーマンスを最大限に高めるための素晴らしい方法です!", + "NeedAccess": "管理者またはスーパー ユーザーのアクセス権を持つユーザーだけが、ウェブサイトの目標を管理できます。", "Optional": "(オプション)", "OverallConversionRate": "%s 総コンバージョン率(目標達成ビジット)", "OverallRevenue": "%s 総収益", @@ -87,6 +94,8 @@ "UpdateGoal": "目標を更新", "URL": "URL", "ViewAndEditGoals": "目標の表示と編集", + "GoalsBy": "%s の目標", + "GoalsAdjective": "目標 %s", "VisitPageTitle": "入力されたタイトルのページを訪問", "VisitsUntilConv": "ビジット数別のコンバージョン", "VisitUrl": "特定の URL を訪問(ページまたはページグループ)", diff --git a/plugins/Goals/lang/ko.json b/plugins/Goals/lang/ko.json index 705ff073c9..8dc370de94 100644 --- a/plugins/Goals/lang/ko.json +++ b/plugins/Goals/lang/ko.json @@ -10,7 +10,14 @@ "BestKeywords": "상위 전환 검색어:", "BestReferrers": "최고 전환 참조 웹사이트:", "CaseSensitive": "대소문자 구분함", + "CancelAndReturnToGoals": "취소하고 %s목표 리스트로 돌아가기%s", + "CategoryTextGeneral_Visitors": "사용자 위치", + "CategoryTextReferrers_Referrers": "리퍼러", + "CategoryTextVisitsSummary_VisitsSummary": "사용자 속성", + "CategoryTextGeneral_Visit": "약속", + "ChooseGoal": "목표 선택", "ClickOutlink": "외부 웹사이트에 대한 링크 클릭", + "SendEvent": "이벤트 보내기", "ColumnAverageOrderRevenueDocumentation": "평균 주문액 (AOV)은 모든 전자상 거래 주문의 총수익을 주문수로 나눈 것입니다.", "ColumnAveragePriceDocumentation": "이 %s의 평균 수익.", "ColumnAverageQuantityDocumentation": "전자상거래 주문에서 판매된 %s의 평균 수량입니다.", @@ -32,6 +39,7 @@ "ConversionsOverview": "전환 개요", "ConversionsOverviewBy": "방문 유형별 전환 개요", "DaysToConv": "일별 전환", + "Details": "목표 상세", "DefaultGoalConvertedOncePerVisit": "(기본) 목표 달성은 방문할 때 마다 한번만 계산", "DefaultRevenue": "목표의 기본 수익은", "DefaultRevenueHelp": "예를 들어 방문자가 전송된 연락처 양식이 평균적으로 10원의 가치가 있을지도 모릅니다. Piwik은 방문자 세그먼트가 얼마나 잘 수행하고 있는지를 이해하는 것을 돕고 있습니다.", @@ -54,30 +62,40 @@ "GoalIsTriggeredWhen": "목표 트리거 시점", "GoalName": "목표 이름", "Goals": "목표", + "ManageGoals": "목표 관리", "GoalsOverview": "목표 개요", "GoalsOverviewDocumentation": "이것은 목표 전환 개요입니다. 먼저 그래프는 모든 총 전환을 보여줍니다. %s 그래프 아래에 각각의 목표에 대한 전환 보고서를 볼 수 있습니다. 스파크 라인은 클릭하여 확대 할 수 있습니다.", "GoalX": "목표 %s", "HelpOneConversionPerVisit": "만약 목표에 일치하는 페이지가 업데이트되거나 한번 이상 방문해도, 첫 번째 방문시에만 목표가 달성됩니다.", "IsExactly": "%s와 일치", + "LearnMoreAboutGoalTrackingDocumentation": "사용자 문서 내 %sPiwik 목표 추적%s 문서를 통해서 좀 더 알 수 있습니다.", "LeftInCart": "카트에 남겨진 %s", + "ManageGoalsOrCreateANewGoal": "%s목표 관리%s 혹은 새로운 목표를 만드세요!", "Manually": "수동으로", "ManuallyTriggeredUsingJavascriptFunction": "JavaScript API의 trackGoal()을 사용하여 수동 트리거", "MatchesExpression": "정규식 %s에 일치", + "NewGoalIntro": "목표 전환 추적은 당신의 비지니스 목적을 측정하고 향상시키는 매우 효과적인 방법입니다.", "NewVisitorsConversionRateIs": "신규 방문자의 전환율은 %s", + "NoGoalsNeedAccess": "주어진 웹사이트는 오직 관리자나 슈퍼 유저 권한을 가진 사용자만이 목표를 관리할 수 있습니다. Piwik의 관리자에게 당신의 웹사이트 목표 설정을 요청하세요. <br>목표 추적은 웹사이트 성능을 이해하고 향상시키는데 큰 도움이 됩니다.", + "NeedAccess": "주어진 웹사이트는 오직 관리자나 슈퍼 유저 권한을 가진 사용자만이 목표를 관리할 수 있습니다.", "Optional": "(선택항목)", "OverallConversionRate": "%s 총 전환율 (목표 달성 방문)", "OverallRevenue": "%s 총 수익", "PageTitle": "페이지 제목", "Pattern": "패턴", + "PluginDescription": "목표를 만들고 목표 전환들 - 시간에 따른 변화, 방문 당 수익, 리퍼러와 키워드 당 전환들 - 에 대한 상세 보고서 보기", "ProductCategory": "제품 카테고리", "ProductName": "제품 이름", "Products": "제품", "ProductSKU": "제품 번호", "ReturningVisitorsConversionRateIs": "재방분 전환율은 %s", "SingleGoalOverviewDocumentation": "이것은 하나의 목표 전환 개요입니다. %s 그래프 아래의 스파크라인은 클릭하여 확대할 수 있습니다.", + "ThereIsNoGoalToManage": "웹사이트 %s에는 관리할 목표가 없습니다.", "UpdateGoal": "목표 갱신", "URL": "URL", "ViewAndEditGoals": "목표 보기 및 편집", + "GoalsBy": "%s가 만든 목표", + "GoalsAdjective": "%s 목표들", "VisitPageTitle": "입력된 제목의 페이지 방문", "VisitsUntilConv": "방문수별 전환", "VisitUrl": "특정 URL 방문 (페이지 또는 페이지 그룹)", diff --git a/plugins/Heartbeat/config/config.php b/plugins/Heartbeat/config/config.php deleted file mode 100644 index 6f7a1417ab..0000000000 --- a/plugins/Heartbeat/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\Heartbeat\Tracker\PingRequestProcessor'), - )), - -); diff --git a/plugins/ImageGraph/API.php b/plugins/ImageGraph/API.php index 4e7b466b21..ffdd223d46 100644 --- a/plugins/ImageGraph/API.php +++ b/plugins/ImageGraph/API.php @@ -122,7 +122,8 @@ class API extends \Piwik\Plugin\API $gridColor = API::DEFAULT_GRID_COLOR, $idSubtable = false, $legendAppendMetric = true, - $segment = false + $segment = false, + $idDimension = false ) { Piwik::checkUserHasViewAccess($idSite); @@ -151,6 +152,9 @@ class API extends \Piwik\Plugin\API if (!empty($idGoal)) { $apiParameters = array('idGoal' => $idGoal); } + if (!empty($idDimension)) { + $apiParameters = array('idDimension' => $idDimension); + } // Fetch the metadata for given api-action $parameters = array( 'idSite' => $idSite, @@ -305,6 +309,7 @@ class API extends \Piwik\Plugin\API 'column' => $plottedMetric, 'language' => $languageLoaded, 'idGoal' => $idGoal, + 'idDimension' => $idDimension, 'legendAppendMetric' => $legendAppendMetric, 'labelUseAbsoluteUrl' => false ); @@ -361,6 +366,7 @@ class API extends \Piwik\Plugin\API 'segment' => $segment, 'apiParameters' => false, 'idGoal' => $idGoal, + 'idDimension' => $idDimension, 'language' => $languageLoaded, 'showTimer' => true, 'hideMetricsDoc' => false, @@ -506,7 +512,10 @@ class API extends \Piwik\Plugin\API if ($idGoal != '') { $idGoal = '_' . $idGoal; } - $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png'; + if ($idDimension != '') { + $idDimension = '__' . $idDimension; + } + $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . $idDimension . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png'; $fileName = str_replace(array(' ', '/'), '_', $fileName); if (!Filesystem::isValidFilename($fileName)) { diff --git a/plugins/Insights/Visualizations/Insight.php b/plugins/Insights/Visualizations/Insight.php index 7fe304e5be..91d2c90b63 100644 --- a/plugins/Insights/Visualizations/Insight.php +++ b/plugins/Insights/Visualizations/Insight.php @@ -40,6 +40,10 @@ class Insight extends Visualization $report = $this->requestConfig->apiMethodToRequestDataTable; $report = str_replace('.', '_', $report); + if (!empty($this->requestConfig->request_parameters_to_modify['reportUniqueId'])) { + $report = $this->requestConfig->request_parameters_to_modify['reportUniqueId']; + } + $this->requestConfig->apiMethodToRequestDataTable = 'Insights.getInsights'; $this->requestConfig->request_parameters_to_modify = array( diff --git a/plugins/Insights/lang/cs.json b/plugins/Insights/lang/cs.json index f389736692..7abc7c7112 100644 --- a/plugins/Insights/lang/cs.json +++ b/plugins/Insights/lang/cs.json @@ -8,7 +8,7 @@ "DayComparedToPreviousWeek": "Stejný den v minulém týdnu", "DayComparedToPreviousYear": "Stejný den v předchozím roce", "Filter": "Filtr", - "FilterIncreaserAndDecreaser": "Zvyšovač & snižovač", + "FilterIncreaserAndDecreaser": "Zvyšovač a snižovač", "FilterOnlyDecreaser": "Pouze snižovač", "FilterOnlyDisappeared": "Pouze ztracení", "FilterOnlyIncreaser": "Pouze zvyšovač", diff --git a/plugins/Insights/lang/ja.json b/plugins/Insights/lang/ja.json index 9695c7fa54..db9cce115b 100644 --- a/plugins/Insights/lang/ja.json +++ b/plugins/Insights/lang/ja.json @@ -1,5 +1,6 @@ { "Insights": { + "PluginDescription": "トラフィックについてのインサイトを提供しています。インサイトは、ダッシュボードウィジェットだけでなく、データの中で最も重要なトレンドを見るためのレポートで新しいアイコンとして使用できます。", "ControlComparedToDescription": "比較する実績値は", "ControlFilterByDescription": "すべて、変動のみ、新規のみ、削除のみを表示", "DatePeriodCombinationNotSupported": "この日付と期間の組み合わせに対するインサイトは作成できません。", diff --git a/plugins/Installation/lang/cs.json b/plugins/Installation/lang/cs.json index b6ffa6dba8..8e32ed6f4a 100644 --- a/plugins/Installation/lang/cs.json +++ b/plugins/Installation/lang/cs.json @@ -4,7 +4,7 @@ "ConfigurationHelp": "Váš konfigurační soubor Piwiku je špatně nastavený. Můžete buď odstranit soubor config\/config.ini a znovu začít instalaci, nebo opravit nastavení databáze.", "ConfirmDeleteExistingTables": "Jste si jistí, že chcete vymazat tabulky: %s z vaší databáze? UPOZORNĚNÍ: DATA Z TĚCHTO TABULEK NEPŮJDOU OBNOVIT!", "Congratulations": "Gratulujeme", - "CongratulationsHelp": "<p>Gratulujeme! Vaše instalace Piwiku je komplentní.<\/p><p>Ujistěte se, že máte JavaScriptový kód vložen do vašich stránek a čekejte na první návštěvníky!<\/p>", + "CongratulationsHelp": "<p>Gratulujeme! Vaše instalace Piwiku je kompletní.<\/p><p>Ujistěte se, že máte JavaScriptový kód vložen do svých stránek a čekejte na první návštěvníky!<\/p>", "DatabaseAbilities": "Schopnosti databáze", "DatabaseCreation": "Vytvoření databáze", "DatabaseErrorConnect": "Nastala chyba při připojování k databázovému serveru", @@ -15,7 +15,7 @@ "DatabaseSetupLogin": "uživatelské jméno", "DatabaseSetupServer": "server s databází", "DatabaseSetupTablePrefix": "prefix tabulek", - "Email": "E-mail", + "Email": "Email", "Extension": "přípona", "Filesystem": "Souborový systém", "GetInvolved": "Pokud se vám líbí, co vidíte, %1$szapojte se%2$s.", @@ -28,12 +28,12 @@ "InvalidStateError": "Chyba: Piwik je již nainstalován. %1$s Jít zpět %2$s do piwiku%3$s.", "JsTagArchivingHelp1": "Pro střední a velké stránky jsou určité optimalizace, které umožňují Piwiku běžet rychleji, jako třeba %1$snastavení automatického archivování%2$s.", "JSTracking_EndNote": "Poznámka: Po dokončení procesu instalace můžete vytvořit vlastní sledovací kód na stránce %1$ssledovací kód%2$s.", - "JSTracking_Intro": "Pokud chcete sledovat Pokud chcete Piwikem sledovat váš provoz na webu, musíte zajistit, že do každé stránky bude přidán extra kód.", + "JSTracking_Intro": "Pokud chcete Piwikem sledovat provoz na vašem webu, musíte zajistit, že do každé stránky bude přidán extra kód.", "LargePiwikInstances": "Nápověda pro velké instalace Piwiku", "Legend": "Legenda", "LoadDataInfileRecommended": "Pokud váš Piwik server zaznamenává provoz na stránkách s vysokým provozem t. j. 100000 stránek za měsíc, doporučujeme se pokusit o řešení tohoto problému.", "LoadDataInfileUnavailableHelp": "Použití %1$s velmi zrychlí archivační proces Piwiku. Pro zpřístupnění zkuste aktualizovat PHP a Mysql a ujistěte se, že má databázový uživatel privilegium %2$s.", - "NfsFilesystemWarning": "Váš server používá soborový systém NFS.", + "NfsFilesystemWarning": "Váš server používá souborový systém NFS.", "NfsFilesystemWarningSuffixAdmin": "To znamená, že Piwik bude při použití sessions založených na souborech velmi pomalý.", "NfsFilesystemWarningSuffixInstall": "Sessions založené na souborech jsou na NFS velmi pomalé. Proto Piwik použije databázové sessions. Pokud bude k nástěnkám přistupovat současně hodně uživatelů, možná budete muset zvýšit maximální počet klientských databázových spojení v konfiguraci databázového serveru.", "NoConfigFound": "Konfigurační soubor Piwiku nebyl nalezen a snažíte se vstoupit na stránku Piwiku.<br \/><b> » Můžete <a href='index.php'>teď nainstalovat<\/a><\/b><br \/><small>Pokud jste Piwik již instalovali a máte v DB nějaké tabulky, nemějte obavy. Můžete je použít a zachovat jejich data.!<\/small>", @@ -48,7 +48,7 @@ "Requirements": "Požadavky Piwiku", "RestartWebServer": "Po uložení změn restartujte Vás web server.", "ReusingTables": "Použití existujících tabulek", - "PiwikOrgNewsletter": "odesílat e-maily s důležitými událostmi v komunitě Piwiku", + "PiwikOrgNewsletter": "odesílat emaily s důležitými událostmi v komunitě Piwiku", "PiwikProNewsletter": "Zasílejte mi informace o službách a nabídkách %sPiwik pro%s", "SeeBelowForMoreInfo": "Pro více informací se podívejte níže.", "SetupWebsite": "Nastavit Web", @@ -57,7 +57,7 @@ "SetupWebsiteSetupSuccess": "Web %s byl úspěšně vytvořen", "SetupWebSiteURL": "URL webových stránek", "SiteSetup": "Prosím vytvořte první stránky, které budou sledovány a analyzovány Piwikem:", - "SiteSetupFootnote": "Poznámka: Jakmile bude instalace Piwiku dokončena, budete moct přidat další stránky ke sledování.", + "SiteSetupFootnote": "Poznámka: Jakmile bude instalace Piwiku dokončena, budete moci přidat další stránky ke sledování.", "SuperUser": "Super uživatel", "SuperUserLogin": "Přihlašovací jméno super uživatele", "SuperUserSetupError": "Při přidávání super uživatele došlo k chybě", @@ -85,7 +85,7 @@ "SystemCheckMbstring": "mbstring", "SystemCheckMbstringHelp": "Rozšíření mbstring je nutné ke zpracování vícebajtových znaků v uživatelském rozhraní a odpovědích API. Také zajistěte, aby byla v souboru php.ini hodnota mbstring.func_overload nastavena na 0.", "SystemCheckMemoryLimit": "Limit paměti", - "SystemCheckMemoryLimitHelp": "Na webech s vysokým provozem, může archivace vyžadovat více paměti něž je nyní povoleno.<br \/>Pokud je potřeba podívejte se na direktivu memory_limit ve vašem souboru php.ini.", + "SystemCheckMemoryLimitHelp": "Na webech s vysokým provozem, může archivace vyžadovat více paměti něž je nyní povoleno.<br \/>Pokud je potřeba, podívejte se na direktivu memory_limit ve vašem souboru php.ini.", "SystemCheckOpenURL": "Otevřít URL", "SystemCheckOpenURLHelp": "Předplatné novinek, informace o aktualizaci a aktualizace jedním kliknutím vyžadují rozšíření \"curl\", allow_url_fopen=On, nebo povolené fsockopen().", "SystemCheckOtherExtensions": "Ostatní rozšíření", @@ -93,20 +93,20 @@ "SystemCheckPageSpeedDisabled": "PageSpeed zakázán", "SystemCheckPageSpeedWarn": "Doporučujeme modul PageSpeed zakázat v konfiguraci webového serveru: %s Bylo hlášeno, že způsobuje problémy s Piwikem.", "SystemCheckPackHelp": "Funkce pack() je nutná ke sledování návštěvnosti v Piwiku.", - "SystemCheckParseIniFileHelp": "Tato vestavěná funkce byla na vašem hostiteli zakázaná. Piwik se pokusí tuto funkci emulovat, ale můžete zaznamenat další bezpečnostní omezení. Výkonnost trackeru bude také omezena.", + "SystemCheckParseIniFileHelp": "Tato vestavěná funkce byla na vašem hostiteli zakázána. Piwik se pokusí tuto funkci emulovat, ale můžete zaznamenat další bezpečnostní omezení. Výkonnost trackeru bude také omezena.", "SystemCheckPdoAndMysqliHelp": "Na GNU\/Linux systému můžete zkompilovat PHP s následujícími volbami: %1$s Přidejte následující řádky do php.ini: %2$s", "SystemCheckPhp": "Verze PHP", "SystemCheckPhpPdoAndMysqli": "Více informací na: %1$sPHP PDO%2$s and %3$sMYSQLI%4$s.", "SystemCheckSplHelp": "Musíte překompilovat PHP s povolenou standardní PHP knihovou (ve výchozím stavu povolena).", "SystemCheckSettings": "Požadovaná konfigurace PHP (php.ini)", - "SystemCheckSummaryNoProblems": "Hurááá! Nejsou zde žádné problémy s nastavením Piwiku. Gratulujeme", + "SystemCheckSummaryNoProblems": "Hurááá! Nejsou zde žádné problémy s nastavením Piwiku. Gratulujeme!", "SystemCheckSummaryThereWereErrors": "Ajaj! Piwik zjistil %1$skritické problémy%2$s s nastavením instalace Piwiku. %3$sTyto problémy musí být okamžitě vyřešeny.%4$s", - "SystemCheckSummaryThereWereWarnings": "JVyskytly se problémy s vaším systémem. Piwik poběží, ale možná se nevyhnete menším problémům.", - "SystemCheckTimeLimitHelp": "na webech s vysokým přenosem, může archivace vyžadovat více času, než je nyní povoleno.<br \/>Pokud je potřeba podívejte se na direktivu max_execution_time ve vašem souboru php.ini", + "SystemCheckSummaryThereWereWarnings": "Vyskytly se problémy s vaším systémem. Piwik poběží, ale možná se nevyhnete menším problémům.", + "SystemCheckTimeLimitHelp": "na webech s vysokým přenosem, může archivace vyžadovat více času, než je nyní povoleno. Pokud je potřeba, podívejte se na direktivu max_execution_time ve svém souboru php.ini", "SystemCheckTracker": "Stav sledování", - "SystemCheckTrackerHelp": "Get požadavek na piwik.php selhal. Zkuste vyloučit tuto URL z HTTP autentizace a zakažte mod_security (možná budete muset požádat vašeho poskytovatele hostingu). Více informací naleznete v záznamu chyb web serveru.", + "SystemCheckTrackerHelp": "Get požadavek na piwik.php selhal. Zkuste vyloučit tuto URL z HTTP autentizace a zakažte mod_security (možná budete muset požádat svého poskytovatele hostingu). Více informací naleznete v záznamu chyb web serveru.", "SystemCheckWarnDomHelp": "Měli byste povolit \"dom\" rozšíření t. j. nainstalovat balíček \"php-dom\" nebo \"php-xml\".", - "SystemCheckWarning": "Piwik bude pracovat normálně, ale některé funkce budou chybět", + "SystemCheckWarning": "Piwik bude pracovat normálně, ale některé funkce mohou chybět", "SystemCheckWarnJsonHelp": "Pro lepší výkon byste měli povolit rozšíření \"json\" t. j. nainstalovat balíček \"php-json\".", "SystemCheckWarnLibXmlHelp": "Protože je vyžadováno dalšími php rozšířeními, měli byste povolit rozšíření \"libxml\" t. j. nainstalovat balíček \"php-libxml\".", "SystemCheckWarnSimpleXMLHelp": "Měli byste povolit rozšíření \"SimpleXML\" t. j. nainstalovat balíček \"php-simplexml\" nebo \"php-xml\".", @@ -116,9 +116,9 @@ "SystemCheckZlibHelp": "Musíte nakonfigurovat a překompilovat PHP s podporou pro \"zlib\", --with-zlib", "SystemCheckCronArchiveProcess": "Archivační cron", "SystemCheckCronArchiveProcessCLI": "Správa procesů pomocí CLI", - "SystemCheckPhpSetting": "Abyste zabránili vážným problémům, ve vašem souboru php.ini musíte nastavit %s", + "SystemCheckPhpSetting": "Abyste zabránili vážným problémům, ve svém souboru php.ini musíte nastavit %s", "SystemCheckUpdateHttps": "Aktualizace přes HTTPS", - "SystemCheckUpdateHttpsNotSupported": "Piwik nemůže použít HTTPS pro aktualizaci, bude použit nezabezpečený protokol HTTP. Ověřte že je podporováno CURL nebo allow_url_fopen a že je nainstalované rozšíření openssl PHP: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/.", + "SystemCheckUpdateHttpsNotSupported": "Piwik nemůže použít HTTPS pro aktualizaci, bude použit nezabezpečený protokol HTTP. Ověřte, že je podporováno CURL nebo allow_url_fopen a že je nainstalované rozšíření openssl PHP: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/.", "NotSupported": "nepodporováno", "Tables": "Vytváření tabulek", "TablesCreatedSuccess": "Tabulky vytvořeny úspěšně!", @@ -132,9 +132,9 @@ "Timezone": "Časové pásmo webových stránek", "WeHopeYouWillEnjoyPiwik": "Doufáme, že si užijete používání Piwiku tak, jako si my užíváme jeho vývoj.", "Welcome": "Vítejte!", - "WelcomeHelp": "<p>Piwik je open source program pro analýzu webu, pomocí kterého můžete jednoduše získat informace, které chcete od vaších návštěvníků.<\/p><p>Tento proces je rozdělen to %s jednoduchých kroků a zabere přibližne 5 minut.<\/p>", + "WelcomeHelp": "<p>Piwik je volně dostupný open source program pro analýzu webu, pomocí kterého můžete jednoduše získávat informace o návštěvnících svého webu.<\/p><p>Tento proces je rozdělen do %s jednoduchých kroků a zabere přibližne 5 minut.<\/p>", "WelcomeToCommunity": "Vítejte v komunitě Piwiku!", "CannotConnectToDb": "Nelze se připojit k databázi", - "CannotConnectToDbResolvingExplanation": "To může být dočasný problém, zkuste %1$sobnovit stránku%2$s. Pokud problém přetrvá, kontaktujte vašeho administrátora Piwiku." + "CannotConnectToDbResolvingExplanation": "To může být dočasný problém, zkuste %1$sobnovit stránku%2$s. Pokud problém přetrvá, kontaktujte svého administrátora Piwiku." } }
\ No newline at end of file diff --git a/plugins/Installation/lang/fr.json b/plugins/Installation/lang/fr.json index 1fea6290ca..d20170b82a 100644 --- a/plugins/Installation/lang/fr.json +++ b/plugins/Installation/lang/fr.json @@ -115,7 +115,7 @@ "SystemCheckZlibHelp": "Vous devez reconfigurer et recompiler PHP avec le support zlib, --with-zlib.", "SystemCheckCronArchiveProcess": "Tâche Cron d'archivage", "SystemCheckCronArchiveProcessCLI": "Gestion des processus via la ligne de commande", - "SystemCheckPhpSetting": "Pour éviter certaines erreurs critique, vous devez définier les entrées suivantes dans votre fichier php.ini : %s", + "SystemCheckPhpSetting": "Pour éviter certaines erreurs critique, vous devez définir les entrées suivantes dans votre fichier php.ini : %s", "SystemCheckUpdateHttps": "Mettre à jour via HTTPS", "SystemCheckUpdateHttpsNotSupported": "Piwik ne peut pas utiliser du HTTPS pour se mettre à jour, il va rétrograder vers une mise à jour utilisant du HTTP non sécurisé. Vérifiez que CURL ou allow_url_fopen est supporté et que l'extesion PHP openssl est installée: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/", "NotSupported": "non supporté", diff --git a/plugins/Installation/lang/ja.json b/plugins/Installation/lang/ja.json index d6d57a8600..a2bfe0e5b4 100644 --- a/plugins/Installation/lang/ja.json +++ b/plugins/Installation/lang/ja.json @@ -15,6 +15,7 @@ "DatabaseSetupLogin": "ログイン名", "DatabaseSetupServer": "データベースサーバー", "DatabaseSetupTablePrefix": "テーブルプレフィックス", + "Email": "メール", "Extension": "エクステンション", "Filesystem": "ファイルシステム", "GetInvolved": "もし Piwik を気にって頂けたら、是非 %1$sget involved%2$s 。", @@ -24,6 +25,7 @@ "InstallationStatus": "インストール状況", "InsufficientPrivilegesHelp": "phpMyAdminのようなツールを使用するか、正しいSQLクエリを実行することにより、これらの権限を追加できます。やり方がわからない場合は、システム管理者に依頼して、権限を付与してもらってください。", "InsufficientPrivilegesMain": "データベースが存在しない ( および作成できない ) 、または具体的なユーザーが十分な権限を持っていない可能性があります。データベースユーザーは、次の権限を有する必要があります。: %s", + "InvalidStateError": "エラー: Piwik が既にインストールされています。Piwik %3$s へ %1$s 戻る %2$s 。", "JsTagArchivingHelp1": "中~高トラフィックのウェブサイトのために、Piwik をより早く(例えば %1$ssetting up auto-archiving%2$s のように) 実行するための最適化の手段があります。", "JSTracking_EndNote": "注:インストールの処理後、%1$sTracking Code%2$s の管理セクションで、トラッキングコードのカスタマイズを行うことができます。", "JSTracking_Intro": "Piwik でウェブトラフィックの追跡をするには、一部の特別なコードが各 web ページに追加されているかどうかを確認する必要があります。", @@ -36,20 +38,28 @@ "NfsFilesystemWarningSuffixInstall": "NFS 上でファイルベースのセッションを利用すると極端に遅いので、Piwik はデータベースセッションを使用します。同時に多くのダッシュボードユーザーがいる場合、データベースサーバーへのクライアント接続の最大数を増やす必要があります。", "NoConfigFound": "Piwik の設定ファイルを見つけることができませんでしたが、あなたは Piwik ページにアクセスしようとしています。<br \/><b> »<a href='index.php'>ただちに Piwik をインストール<\/a>することができます。<\/b><br \/><small>以前に Piwik をインストールしたことがあり、DB に多少のデータがある場合でも心配しないでください。 既存のデータを保持したまま、同じテーブルを再利用することができます!<\/small>", "Optional": "オプション", + "Password": "パスワード", "PasswordDoNotMatch": "パスワードが一致しませんでした", + "PasswordRepeat": "パスワード (再入力)", "PercentDone": "%s %% 完了", "PleaseFixTheFollowingErrors": "次のエラーを修正してください", + "DefaultSettings": "Piwik のデフォルトの設定", + "DefaultSettingsHelp": "Piwik の設定はデフォルトです。後で管理画面からカスタマイズすることができます。", "Requirements": "Piwik の動作環境", "RestartWebServer": "この変更を行った後、ウェブサーバーを再起動してください。", "ReusingTables": "表の再利用", "PiwikOrgNewsletter": "私に Piwik コミュニティの最新情報をメールで送る", + "PiwikProNewsletter": "%s Piwik PRO %s のサービスや最新情報を送ってください", "SeeBelowForMoreInfo": "詳細は、以下をご確認ください。", "SetupWebsite": "ウェブサイトのセットアップ", "SetupWebsiteError": "ウェブサイトを追加する際にエラーが発生しました", + "SetupWebSiteName": "ウェブサイトの名前", "SetupWebsiteSetupSuccess": "ウェブサイト %s は正常に作成されました!", + "SetupWebSiteURL": "ウェブサイトの URL", "SiteSetup": "Piwik で追跡・分析したい初めのウェブサイトを設定してください。", "SiteSetupFootnote": "注: Piwik のインストールが完了すると、さらに追跡対象のウェブサイトを追加することができるようになります!", "SuperUser": "スーパーユーザー", + "SuperUserLogin": "スーパーユーザーのログイン", "SuperUserSetupError": "スーパーユーザーの追加時に、エラーが発生しました。", "SuperUserSetupSuccess": "スーパーユーザーの作成に成功しました!", "SystemCheck": "システムの確認", @@ -63,6 +73,7 @@ "SystemCheckExtensions": "その他の必須エクステンション", "SystemCheckFileIntegrity": "ファイルの整合性", "SystemCheckFunctions": "必須関数", + "SystemCheckFunctionHelp": "あなたは、このビルトイン関数を有効にする必要があります。", "SystemCheckGDFreeType": "GD > 2.x + Freetype (graphics)", "SystemCheckGDHelp": "スパークライン(小さなグラフ)は動作しません。", "SystemCheckGlobHelp": "この組み込み関数はホストで無効化されています。 Piwik はこの関数のエミュレートを試みますが、さらなるセキュリティ制限に遭遇する場合があります。 また、機能性にも影響を与える場合があります。", @@ -93,6 +104,7 @@ "SystemCheckSummaryThereWereWarnings": "システムにいくつかの問題があります。Piwik は実行できますが、いくつか小さな問題がみられる可能性があります。", "SystemCheckTimeLimitHelp": "アクセス数の多いウェブサイトでは、アーカイブ処理の実行に、現在許可されている以上に多くの時間を必要とする場合があります。<br \/>必要であれば、php.ini ファイルのディレクティブ max_execution_time を参照してください。", "SystemCheckTracker": "トラッカー状況", + "SystemCheckTrackerHelp": "失敗した piwik.php へのリクエストを取得します。HTTP 認証からのこの URL のホワイト リスト化を試みて、mod_security を無効にします(ウェブホストに依頼する必要があります)。エラーの詳細については、web サーバーのエラー ログ ファイルを確認します。", "SystemCheckWarnDomHelp": "\"dom\" エクステンションを有効にする必要があります(例えば \"php-dom\" と \"php-xml\" パッケージのどちらか、あるいは両方をインストールする)。", "SystemCheckWarning": "Piwik は正常に動作しますが、いくつかの機能は動作しないかもしれません。", "SystemCheckWarnJsonHelp": "より良いパフォーマンスのために、\"json\" エクステンションを有効にする必要があります(例えば \"php-json\" パッケージをインストールする)。", @@ -117,6 +129,7 @@ "TablesUpdatedSuccess": "データベースの %1$s から %2$s へのアップデートが成功しました。", "TablesWarningHelp": "既存のデータベーステーブルを再利用するか、データベース内の全データを消去してクリーンインストールするかを選択してください。", "TablesWithSameNamesFound": "%1$s既存のデータベース内のいくつかのテーブル%2$sが、Piwik が作成しようとしているテーブルと同じ名前を持っています", + "Timezone": "ウェブサイトのタイムゾーン", "WeHopeYouWillEnjoyPiwik": "私達が Piwik の開発を楽しんでいるように、Piwik のご利用をお楽しみ頂ける事を願っています。", "Welcome": "ようこそ!", "WelcomeHelp": "<p>Piwik は、ビジターからあなたが必要とする情報を収集することを容易にする、オープンソースのウェブ解析ソフトウェアです。<\/p><p>この処理は %s の簡単なステップに分かれていて、5分程度で完了します。<\/p>", diff --git a/plugins/Installation/lang/ko.json b/plugins/Installation/lang/ko.json index ff083470d6..3fa7374b09 100644 --- a/plugins/Installation/lang/ko.json +++ b/plugins/Installation/lang/ko.json @@ -1,5 +1,6 @@ { "Installation": { + "CollaborativeProject": "Piwik는 전세계 사람들과 함께 만들어 나가는 협동 프로젝트입니다.", "ConfigurationHelp": "Piwik 설정 파일이 잘못 설정되어있는 것 같습니다. config\/config.ini.php를 제거하고 설치를 다시 시작하거나 데이터베이스의 연결 설정을 수정하세요.", "ConfirmDeleteExistingTables": "테이블 %s 을(를) 데이터베이스로부터 삭제하기를 원합니까? 경고: 이 테이블의 데이터는 복구되지 않습니다!", "Congratulations": "축하합니다", @@ -14,12 +15,20 @@ "DatabaseSetupLogin": "로그인", "DatabaseSetupServer": "데이터베이스 서버", "DatabaseSetupTablePrefix": "테이블 접두사", + "Email": "이메일", "Extension": "확장", "Filesystem": "파일시스템", + "GetInvolved": "만약 이를 좋아한다면, 언제라도 %1$s참여할 수 있습니다%2$s.", "GoBackAndDefinePrefix": "돌아가서 Piwik 테이블을 위해 Prefix를 정의합니다", + "HappyAnalysing": "즐거운 분석!", "Installation": "설치", "InstallationStatus": "설치 상태", "InsufficientPrivilegesHelp": "phpMyAdmin 같은 도구를 사용하거나 올바른 SQL 쿼리를 실행하여 이러한 권한을 추가 할 수 있습니다. 방법을 잘 모를 경우 시스템 관리자에게 문의하여 권한을 부여 받으세요.", + "InsufficientPrivilegesMain": "데이터베이스가 존재하지 않거나 (혹은 생성되지 않았거나), 지정된 사용자가 충분한 권한을 가지고 있지 않습니다. 데이터베이스 사용자는 반드시 아래의 권한을 가지고 있어야 합니다: %s", + "InvalidStateError": "에러: Piwik가 이미 설치되어 있습니다. %1$s 뒤돌아 가기 %2$s 설치된 Piwik로...%3$s.", + "JsTagArchivingHelp1": "중간의 혹은 높은 트래픽을 가진 웹사이트를 위해 Piwik를 빠르게 동작시키는 여러 최적화 기법(예: %1$s자동-아카이브 작업 설정%2$s)이 제공되고 있습니다.", + "JSTracking_EndNote": "참고: 인스톨 과정이 끝난 후 %1$s트래킹 코드%2$s 관리자 화면에서 맞춤 트래킹 코드를 생성할 수 있습니다.", + "JSTracking_Intro": "웹사이트 내 트래픽을 추적하기 위해서 당신의 각 웹페이지에 코드를 추가해야 합니다.", "LargePiwikInstances": "높은 트래픽 환경에서 Piwik를 사용하기 위한 팁", "Legend": "범례", "LoadDataInfileRecommended": "트래픽이 높은 웹사이트를 Piwik 서버에서 추적하려는 경우 (예: 한달 100,000 페이지뷰 이상), 이 문제를 해결하고 사용할 것을 권장합니다.", @@ -29,22 +38,34 @@ "NfsFilesystemWarningSuffixInstall": "NFS의 파일 기반 세션을 사용하면 매우 느립니다, 그래서 Piwik 데이터베이스 세션을 사용합니다. 대시보드 사용자가 많은 경우는, 데이터베이스 서버에 클라이언트 연결의 최대 수를 증가해야 할지도 모릅니다.", "NoConfigFound": "Piwik 설정 파일을 찾을 수 없으며 당신은 Piwik 페이지로 접근을 계속 시도하고 있습니다.<br \/><b> » <a href='index.php'>지금 Piwik을 설치하세요.<\/a><\/b><br \/><small>만약 Piwik을 이전에 설치한 적이 있고 DB에 테이블이 좀 있다면 걱정하지마세요, 같은 테이블을 재사용할 수 있고 현재 데이터를 보존할 수 있습니다!<\/small>", "Optional": "옵션", + "Password": "비밀번호", "PasswordDoNotMatch": "비밀번호가 맞지 않습니다", + "PasswordRepeat": "비밀번호 (반복 입력)", "PercentDone": "%s %% 완료", "PleaseFixTheFollowingErrors": "다음 오류를 수정하세요", + "DefaultSettings": "기본 Piwik 설정", + "DefaultSettingsHelp": "Piwik 기본 설정에 따릅니다. 이를 지금 혹은 후에 관리자 화면에서 변경할 수 있습니다.", "Requirements": "Piwik 요구 사항", "RestartWebServer": "변경한 후 웹서버를 다시 시작하세요.", + "ReusingTables": "테이블", + "PiwikOrgNewsletter": "주요 Piwik 커뮤니티의 업데이트 이메일 구독", + "PiwikProNewsletter": "%sPiwik PRO%s 서비스 및 신청과 관련한 정보 받기", "SeeBelowForMoreInfo": "자세한 내용은 아래를 참조하세요.", "SetupWebsite": "웹사이트 설정", "SetupWebsiteError": "웹사이트 추가중에 에러가 있었습니다", + "SetupWebSiteName": "웹사이트", "SetupWebsiteSetupSuccess": "웹사이트 %s가 성공적으로 작성되었습니다!", - "SiteSetup": "Piwik으로 추적 및 분석하려는 첫 번째 웹사이트를 설정하하세요:", + "SetupWebSiteURL": "웹사이트 URL", + "SiteSetup": "Piwik으로 추적 및 분석하려는 첫 번째 웹사이트를 설정하세요:", "SiteSetupFootnote": "참고: Piwik 설치가 완료되면, 더 많은 웹사이트를 추적할 수 있습니다!", - "SuperUser": "수퍼 유저", - "SuperUserSetupSuccess": "수퍼 유저가 만들어졌습니다!", + "SuperUser": "슈퍼 유저", + "SuperUserLogin": "슈퍼 유저 로그인", + "SuperUserSetupError": "슈퍼 유저를 추가함에 있어 문제가 발생했습니다.", + "SuperUserSetupSuccess": "슈퍼 유저가 만들어졌습니다!", "SystemCheck": "시스템 체크", "SystemCheckAutoUpdateHelp": "주의: Piwik의 원클릭 업데이트는 Piwik 폴더와 파일에 쓰기 권한이 필요합니다.", "SystemCheckCreateFunctionHelp": "Piwik은 콜백에 익명 함수를 사용합니다.", + "SystemCheckDatabaseExtensions": "MySQL 확장", "SystemCheckDatabaseHelp": "Piwik은 mysqli 확장하거나 PDO와 pdo_mysql 확장의 둘 중 하나가 필요합니다.", "SystemCheckDebugBacktraceHelp": "View::factory는 모듈을 호출하여 뷰를 만들 수 없습니다.", "SystemCheckError": "에러 발생 - 진행 전에 수정이 필요함", @@ -52,30 +73,38 @@ "SystemCheckExtensions": "필수 확장", "SystemCheckFileIntegrity": "파일 무결성", "SystemCheckFunctions": "필수 기능", + "SystemCheckFunctionHelp": "빌트인 함수들을 활성화해야 합니다.", + "SystemCheckGDFreeType": "GD > 2.x + Freetype (그래픽)", "SystemCheckGDHelp": "스파크라인(소규모 그래프)은 작동하지 않습니다.", "SystemCheckGlobHelp": "이 내장 함수는 호스트에서 비활성화되어 있습니다. Piwik이 함수의 에뮬레이션을 시도하지만, 새로운 보안 제한으로 발생하는 경우가 있습니다. 또한 기능에 영향을 미칠수 있습니다.", "SystemCheckGzcompressHelp": "zlib 확장과 gzcompress() 함수를 활성화해야합니다.", "SystemCheckGzuncompressHelp": "zlib 확장과 gzuncompress() 함수를 활성화해야합니다.", "SystemCheckIconvHelp": "\"iconv\"의 지원을 활성화하고, PHP 재구축 설정을 해야합니다 --with-iconv.", + "SystemCheckJsonHelp": "Piwik가 JSON 데이터를 읽고 쓰기 위해서 php5-json 확장이 필요합니다.", "SystemCheckMailHelp": "mail() 없이 피드백과 잊어버린 비밀번호 메세지는 보내지지 않습니다.", "SystemCheckMbstring": "mbstring", + "SystemCheckMbstringHelp": "mbstring 확장은 사용자 인터페이스와 API 응답 내 멀티바이트 문자열을 다루는데 필요합니다. 또한, php.ini 내 mbstring.func_overload이 0으로 되어 있는지 확인하세요.", "SystemCheckMemoryLimit": "메모리 제한", "SystemCheckMemoryLimitHelp": "높은 트래픽의 웹사이트에서는 압축 진행중에 현재 허용된 것보다 더 높은 메모리를 필요로 할 수 있습니다. 만약 필요하다면 php.ini 파일에서 memory_limit을 변경하세요.", "SystemCheckOpenURL": "URL 열기", "SystemCheckOpenURLHelp": "뉴스 레터 구독, 업데이트 알림, 원 클릭 업데이트는 \"curl\"확장, allow_url_fopen = On 또는 fsockopen()이 유효해야합니다.", "SystemCheckOtherExtensions": "기타 확장", "SystemCheckOtherFunctions": "기타 함수", + "SystemCheckPageSpeedDisabled": "PageSpeed 비활성화", + "SystemCheckPageSpeedWarn": "우리는 웹서버 %s 내 PageSpeed 모듈을 비활성화 하는 것을 추천합니다. 그간 Piwik에 여러 문제들이 PageSpeed와 연관있었습니다.", "SystemCheckPackHelp": "pack() 함수는 Piwik에서 방문자를 추적하는 데 필요합니다.", "SystemCheckParseIniFileHelp": "이 내장 함수는 호스트에서 비활성화되어 있습니다. Piwik이 함수의 에뮬레이션을 시도하지만, 새로운 보안 제한으로 발생하는 경우가 있습니다. 또한 트래커 성능에도 영향을 미칩니다.", "SystemCheckPdoAndMysqliHelp": "Linux 서버는 다음 옵션으로 PHP를 컴파일합니다: %1$sphp.ini에 다음 줄을 추가합니다: %2$s", "SystemCheckPhp": "PHP 버전", "SystemCheckPhpPdoAndMysqli": "자세한 문서: %1$sPHP PDO%2$s and %3$sMYSQLI%4$s.", "SystemCheckSplHelp": "스탠다드 PHP 라이브러리가 활성화된 상태로 PHP를 설정하고 리빌딩해야 합니다. (기본).", + "SystemCheckSettings": "요구되는 PHP 설정 (php.ini)", "SystemCheckSummaryNoProblems": "만세! Piwik 설정에 아무런 문제가 없습니다. 스스로에게 칭찬하세요.", "SystemCheckSummaryThereWereErrors": "헐! Piwik 설정에서 일부 %1$s심각한 오류%2$s를 감지했습니다. %3$s이 문제는 즉시 해결해야합니다.%4$s", "SystemCheckSummaryThereWereWarnings": "시스템에 몇몇 문제가 있습니다. Piwik가 설치되었지만 몇가지 사소한 문제가 발생할 수 있습니다.", "SystemCheckTimeLimitHelp": "높은 트래픽의 웹사이트에서는 압축 진행중에 현재 허용된 것보다 더 많은 시간을 필요로 할 수 있습니다. 만약 필요하다면 php.ini 파일에서 max_execution_time을 변경하세요..", "SystemCheckTracker": "추적기 상태", + "SystemCheckTrackerHelp": "piwik.php에서 GET 리퀘스트가 실패하였습니다. 현재 URL을 HTTP Authentication의 화이트 리스트에 추가해주시고 mod_security를 꺼주시길 바랍니다(이는 당신의 웹페이지 관리자에게 연락해야 할 것입니다). 이번 에러와 관련하여 웹 서버의 에러 로그 파일을 확인하실 것을 권장합니다.", "SystemCheckWarnDomHelp": "\"dom\"확장을 활성화해야합니다 (예: \"php-dom\"과 \"php-xml\"패키지 중 하나 또는 모두 설치).", "SystemCheckWarning": "Piwik 은 정상적으로 작동하지만 몇몇의 기능이 작동하지 않을 수 있습니다.", "SystemCheckWarnJsonHelp": "더 나은 성능을 위해 \"json\"확장을 활성화해야합니다 (예: \"php-json\"패키지를 설치).", @@ -85,15 +114,27 @@ "SystemCheckWriteDirs": "쓰기 접근 가능 디렉토리", "SystemCheckWriteDirsHelp": "리눅스 시스템에서 이 에러를 고치기 위해서, 다음 명령어를 입력해보세요.", "SystemCheckZlibHelp": "zlib 지원 활성화된 상태로 PHP를 설정하고 리빌빙해야 합니다.", + "SystemCheckCronArchiveProcess": "아카이브 cron", + "SystemCheckCronArchiveProcessCLI": "CLI를 통해서 프로세스 관리", + "SystemCheckPhpSetting": "치명적인 문제를 막기 위해, 다음의 것을 php.ini 파일에 설정하셔야 합니다: %s", + "SystemCheckUpdateHttps": "HTTPS를 통한 업데이트", + "SystemCheckUpdateHttpsNotSupported": "Piwik는 업데이트 시 HTTPS를 사용할 수 없으며, 안전하지 않은 HTTP 업데이트로 전환될 것입니다. CURL 혹은 allow_url_fopen을 확인하시고, 또한 openssl PHP 확장이 설치되어 있는지 확인하세요.: http:\/\/piwik.org\/faq\/troubleshooting\/faq_177\/", + "NotSupported": "지원되지 않음", "Tables": "테이블 생성", "TablesCreatedSuccess": "테이블 생성을 성공적으로 마쳤습니다!", "TablesDelete": "감지된 테이블 삭제", "TablesDeletedSuccess": "존재하는 Piwik 테이블을 성공적으로 삭제했습니다", "TablesFound": "데이터베이스에서 다음의 테이블을 찾았습니다", "TablesReuse": "존재하는 테이블을 재사용", + "TablesUpdatedSuccess": "데이터베이스가 %1$s에서 %2$s로 성공적으로 업데이트 되었습니다.", "TablesWarningHelp": "존재하는 데이터베이스 테이블을 재사용하거나 모든 존재하는 데이터를 삭제하고 클린 설치를 할 지 선택하세요.", "TablesWithSameNamesFound": "몇몇의 테이블: %1$s \/ 데이터베이스: %2$s 가 Piwik이 생성하려고 하는 테이블 이름과 같습니다.", + "Timezone": "웹사이트 시간대", + "WeHopeYouWillEnjoyPiwik": "당신이 Piwik를 사용함에 있어 즐기는 만큼 우리는 이를 만듦에 즐거움을 느낀다.", "Welcome": "환영합니다!", - "WelcomeHelp": "<p>Piwik 은 오픈 소스 웹 분석 소프트웨어로써 쉽고 원하는 정보를 당신의 방문자들로부터 얻을 수 있게 해줍니다.<\/p><p>이 과정은 %s 의 쉬운 단계로 나뉘어 있고 5분 정도만 소요됩니다.<\/p>" + "WelcomeHelp": "<p>Piwik 은 오픈 소스 웹 분석 소프트웨어로써 쉽고 원하는 정보를 당신의 방문자들로부터 얻을 수 있게 해줍니다.<\/p><p>이 과정은 %s 의 쉬운 단계로 나뉘어 있고 5분 정도만 소요됩니다.<\/p>", + "WelcomeToCommunity": "Piwik 커뮤니티에 오신 걸 환영합니다!", + "CannotConnectToDb": "데이터베이스에 연결할 수 없습니다.", + "CannotConnectToDbResolvingExplanation": "이것은 일시적인 문제일 것입니다. %1$s페이지 새로고침%2$s을 해보세요. 만약 문제가 지속적으로 발생한다면 Piwik 관리자에게 연락하세요." } }
\ No newline at end of file diff --git a/plugins/Installation/lang/nb.json b/plugins/Installation/lang/nb.json index 136a352d20..fcc75d8190 100644 --- a/plugins/Installation/lang/nb.json +++ b/plugins/Installation/lang/nb.json @@ -1,5 +1,7 @@ { "Installation": { + "CollaborativeProject": "Piwik er et samarbeidsprosjekt, bygget med kjærlighet av folk fra hele verden.", + "ConfigurationHelp": "Det ser ut til at det er en feil i din konfigurasjonsfil for Piwik. Du kan enten slette config\/config.ini.php og fortsette installasjonen, eller endre tilkoblingsinnstillingene til databasen.", "ConfirmDeleteExistingTables": "Er du sikker på at du vil slette tabellene %s fra databasen din? ADVARSEL: DATA FRA DISSE TABELLENE KAN IKKE GJENOPPRETTES!", "Congratulations": "Gratulerer", "CongratulationsHelp": "<p>Gratulerer! Din Piwik installasjon er fullført.<\/p><p>Sørg for at JavaScript-koden er lagt inn på dine sider, og vent på dine første besøkende!<\/p>", @@ -8,36 +10,52 @@ "DatabaseErrorConnect": "Klarte ikke koble til databasetjeneren", "DatabaseServerVersion": "Databasetjener-versjon", "DatabaseSetup": "Databaseinnstillinger", - "DatabaseSetupAdapter": "adapter", - "DatabaseSetupDatabaseName": "databasenavn", - "DatabaseSetupLogin": "brukernavn", - "DatabaseSetupServer": "databasetjener", - "DatabaseSetupTablePrefix": "tabellprefiks", + "DatabaseSetupAdapter": "Adapter", + "DatabaseSetupDatabaseName": "Databasenavn", + "DatabaseSetupLogin": "Brukernavn", + "DatabaseSetupServer": "Databasetjener", + "DatabaseSetupTablePrefix": "Tabellprefiks", "Email": "E-post", "Extension": "utvidelse", "Filesystem": "Filsystem", + "GetInvolved": "Hvis du liker det du ser, kan du %1$sinvolvere deg%2$s.", "GoBackAndDefinePrefix": "Gå tilbake og definer et prefiks for Piwik-tabellene", + "HappyAnalysing": "God analyse!", "Installation": "Installasjon", "InstallationStatus": "Installasjonsstatus", + "InsufficientPrivilegesHelp": "Du kan legge til disse privilegiene ved å bruke et verktøy som phpMyAdmin, eller å kjøre de rette SQL-spørringene. Hvis du ikke vet hvordan du gjør disse tingene, vennligst spør din sysadmin om å hjelpe deg.", + "InsufficientPrivilegesMain": "Enten finnes ikke databasen (og kunne ikke opprettes), eller så har brukeren ikke tilstrekkelige rettigheter. Databasebrukeren må ha de følgende rettighetene: %s", "InvalidStateError": "Feil: Piwik er allerede installert. %1$s Gå tilbake %2$s til Piwik%3$s.", + "JsTagArchivingHelp1": "For nettsteder med medium til høy trafikk er det noen optimaliseringer som bør gjøres for å hjelpe Piwik å kjøre raskere (slik som å %1$ssette opp auto-arkivering%2$s).", + "JSTracking_EndNote": "Merk: etter installasjonsprosessen kan du generere tilpassede sporingskoder i adminseksjonen %1$sSporingskode%2$s.", + "JSTracking_Intro": "For å spore nettrafikk med Piwik må du legge til litt ekstra kode på alle sider på ditt nettsted.", "LargePiwikInstances": "Hjelp for store Piwik-instanser", "Legend": "Forklaring", - "NfsFilesystemWarning": "Din tjeneren bruker et NFS filsystem.", + "LoadDataInfileRecommended": "Hvis din Piwik-server sporer nettsider med mye trafikk (for eksempel >100 000 sider per måned), anbefaler vi deg å prøve å fikse dette problemet.", + "LoadDataInfileUnavailableHelp": "Å bruke %1$s vil gjøre arkiveringsprosessen til Piwik mye raskere. For å gjøre det tilgjengelig, prøv å oppdatere PHP og MySQL og forsikre deg om at databasebrukeren har rettigheten %2$s.", + "NfsFilesystemWarning": "Din tjeneren bruker et NFS-filsystem.", "NfsFilesystemWarningSuffixAdmin": "Dette betyr Piwik vil være svært treg når du bruker filbaserte økter.", - "NoConfigFound": "Klarte ikke finne konfigurasjonsfilen for Piwik, og du prøver å få tilgang til en Piwik-side. <br \/><b> »Du kan <a href='index.php'>installere Piwik nå<\/a><\/b><br \/><small>Hvis du prøvde å installere Piwik tidligere og har noen tabeller i databasen, kan du ta det med ro. Du kan bruke de gamle tabellene og beholde dine eksisterende data!<\/small>", + "NfsFilesystemWarningSuffixInstall": "Å bruke filbaserte sesjoner på NFS er ekstremt tregt, så Piwik vil bruke databasesesjoner. Hvis det er mange som ser på kontrollpanelene samtidig, kan det tenkes at du må øke maks antall klienttilkoblinger som er tillatt på databaseserveren.", + "NoConfigFound": "Klarte ikke finne konfigurasjonsfilen for Piwik, og du prøver å få tilgang til en Piwik-side. <br \/><b> » Du kan <a href='index.php'>installere Piwik nå<\/a><\/b><br \/><small>Hvis du prøvde å installere Piwik tidligere og har noen tabeller i databasen, kan du ta det med ro. Du kan bruke de gamle tabellene og beholde dine eksisterende data!<\/small>", "Optional": "Valgfritt", "Password": "Passord", "PasswordDoNotMatch": "passordene stemmer ikke overens", "PasswordRepeat": "Passord (gjenta)", "PercentDone": "%s %% ferdig", "PleaseFixTheFollowingErrors": "Fiks følgende feil", + "DefaultSettings": "Standard Piwik-innstillinger", + "DefaultSettingsHelp": "Piwik kommer med standardinnstillinger. Du kan tilpasse dem nå eller gjøre det senere i administrasjonsskjermen.", "Requirements": "Krav for å kjøre Piwik", + "RestartWebServer": "Etter å ha gjort denne endringen må du restarte webserveren.", "ReusingTables": "Gjenbruker tabellene", + "PiwikOrgNewsletter": "Send meg e-poster med viktige oppdateringer om Piwik", "PiwikProNewsletter": "send meg informasjon om %sPiwik PRO%s tjenester og tilbud", "SeeBelowForMoreInfo": "Se nedenfor for mer informasjon.", "SetupWebsite": "Legg til et nettsted", "SetupWebsiteError": "Det skjedde en feil da nettstedet ble lagt til.", + "SetupWebSiteName": "Nettstedsnavn", "SetupWebsiteSetupSuccess": "Nettstedet %s ble opprettet.", + "SetupWebSiteURL": "Nettsteds-URL", "SuperUser": "Superbruker", "SuperUserSetupSuccess": "Superbruker ble opprettet!", "SystemCheck": "Systemsjekk", diff --git a/plugins/Installation/lang/tr.json b/plugins/Installation/lang/tr.json index 33f074fa7d..2b51d7634d 100644 --- a/plugins/Installation/lang/tr.json +++ b/plugins/Installation/lang/tr.json @@ -2,27 +2,38 @@ "Installation": { "Congratulations": "Tebrikler", "CongratulationsHelp": "<p>Tebrikler! Piwik Başarıyla kuruldu.<\/p><p>JavaScript kodunu sayfalarınıza yerleştirdiğinizden emin olun ve ilk ziyaretçilerinizi beklemeye başlayın!<\/p>", - "DatabaseCreation": "Veritabaı oluşturma", + "DatabaseCreation": "Veritabanı oluşturma", + "DatabaseErrorConnect": "Veritabanına bağlanırken hata oluştu", "DatabaseServerVersion": "Veritabanı sunucu versiyonu", "DatabaseSetup": "Veritabanı Yüklemesi", "DatabaseSetupDatabaseName": "Veritabanı adı", "DatabaseSetupLogin": "Giriş", "DatabaseSetupServer": "Veritabanı Sunucusu", "DatabaseSetupTablePrefix": "Tablo öneki", + "Email": "E-posta", "Extension": "eklenti", "Filesystem": "Dosya sistemi", "Installation": "Yükleme", "InstallationStatus": "Yükleme durumu", + "InvalidStateError": "Hata: Piwik zaten kurulmuş. %1$s Geri dön %2$s Piwik%3$s.", "LargePiwikInstances": "Büyük Piwik örnekleri için yardım", "Legend": "Gösterge", "NfsFilesystemWarning": "Sunucunuz NFS dosya sistemini kullanıyor.", "Optional": "Opsiyonel", + "Password": "Şifre", "PasswordDoNotMatch": "Girilen şifreler aynı değil.", + "PasswordRepeat": "Şifre (Tekrardan)", "PercentDone": "%s %% tamamlandı", + "PleaseFixTheFollowingErrors": "Oluşan hataları düzeltin lütfen.", + "DefaultSettings": "Varsayılan Piwik ayarları", + "DefaultSettingsHelp": "Piwik varsayılan ayarlarla gelecektir. Şimdi özelleştirebilirsiniz yada daha sonra yönetici ekranından da yapabilirsiniz.", "Requirements": "Piwik gereklilikleri", + "RestartWebServer": "Bu değişiklikten sonra, web sunucunuzu yeniden başlatın.", "SetupWebsite": "Web sitesi ekle", "SetupWebsiteError": "Web site eklenirken bir sorun oluştu.", + "SetupWebSiteName": "Web site adı", "SetupWebsiteSetupSuccess": "Web site %s başarıyla eklendi.", + "SetupWebSiteURL": "Web site adresi", "SystemCheck": "Sistem kontrolü", "SystemCheckDatabaseHelp": "Piwik mysqli eklentisi ya da PDO ve pdo_mysql eklentilerine ihtiyaç duyar.", "SystemCheckError": "Bir hata oluştu. Devam edebilmek için hatayı gidermelisiniz.", @@ -32,11 +43,20 @@ "SystemCheckMbstring": "mbstring", "SystemCheckMemoryLimit": "Hafıza limiti", "SystemCheckOtherExtensions": "Diğer eklentiler", + "SystemCheckOtherFunctions": "Diğer fonksiyonlar", + "SystemCheckPageSpeedDisabled": "Sayfa Hızı devre dışı", "SystemCheckPhp": "PHP versiyonu", + "SystemCheckPhpPdoAndMysqli": "Daha fazla bilgi için: %1$sPHP PDO%2$s ve %3$sMYSQLI%4$s.", + "SystemCheckSettings": "Gerekli PHP yapılandırmaları (php.ini)", + "SystemCheckSummaryNoProblems": "Vayy! Piwik kurulumunda hiçbir sorun bulunamadı. Sırtınızı sıvazlayabiliriz.", "SystemCheckWarning": "Piwik normal olarak çalışacaktır ancak bazı özellikler eksik olacak.", + "NotSupported": "desteklenmiyor", "Tables": "Tablolar oluşturuluyor", - "TablesCreatedSuccess": "Tablolar başarılı bir şekilde yaratıldı!", + "TablesCreatedSuccess": "Tablolar başarılı bir şekilde oluşturuldu!", + "Timezone": "Web site zaman dilimi", "Welcome": "Hoşgeldiniz!", - "CannotConnectToDb": "Veritabanına bağlanamadı" + "WelcomeToCommunity": "Piwik topluluğuna hoşgeldiniz!", + "CannotConnectToDb": "Veritabanına bağlanamadı", + "CannotConnectToDbResolvingExplanation": "Bu geçici bir sorun olabilir, %1$ssayfayı yenilemeyi deneyin%2$s. Eğer sorun devam ederse Piwik yöneticinize başvurun." } }
\ No newline at end of file diff --git a/plugins/Intl/Commands/GenerateIntl.php b/plugins/Intl/Commands/GenerateIntl.php index 45637452a8..d9710f5977 100644 --- a/plugins/Intl/Commands/GenerateIntl.php +++ b/plugins/Intl/Commands/GenerateIntl.php @@ -262,7 +262,9 @@ class GenerateIntl extends ConsoleCommand $translations['Intl']['Time_AM'] = $calendarData['dayPeriods']['format']['wide']['am']; $translations['Intl']['Time_PM'] = $calendarData['dayPeriods']['format']['wide']['pm']; - $translations['Intl']['Format_Time'] = $calendarData['timeFormats']['medium']; + $translations['Intl']['Format_Time'] = '{time}'; + $translations['Intl']['Format_Time_12'] = $calendarData['dateTimeFormats']['availableFormats']['hms']; + $translations['Intl']['Format_Time_24'] = $calendarData['dateTimeFormats']['availableFormats']['Hms']; $translations['Intl']['Format_Date_Long'] = $calendarData['dateFormats']['full']; $translations['Intl']['Format_Date_Day_Month'] = $calendarData['dateTimeFormats']['availableFormats']['MMMEd']; $translations['Intl']['Format_Date_Short'] = $calendarData['dateFormats']['medium']; @@ -273,8 +275,8 @@ class GenerateIntl extends ConsoleCommand } $translations['Intl']['Format_Year'] = $calendarData['dateTimeFormats']['availableFormats']['y']; - $translations['Intl']['Format_DateTime_Long'] = $calendarData['dateFormats']['full'] . ' ' . $calendarData['timeFormats']['medium']; - $translations['Intl']['Format_DateTime_Short'] = $calendarData['dateFormats']['medium'] . ' ' . $calendarData['timeFormats']['medium']; + $translations['Intl']['Format_DateTime_Long'] = $calendarData['dateFormats']['full'] . ' {time}'; + $translations['Intl']['Format_DateTime_Short'] = $calendarData['dateFormats']['medium'] . ' {time}'; $translations['Intl']['Format_Interval_Long_D'] = $this->transformDateFormat($calendarData['dateTimeFormats']['intervalFormats']['yMMMd']['d'], array('MMMM' => 'MMM', 'LLLL' => 'LLL', 'MMM' => 'MMMM', 'LLL' => 'LLLL')); $translations['Intl']['Format_Interval_Long_M'] = $this->transformDateFormat($calendarData['dateTimeFormats']['intervalFormats']['yMMMd']['M'], array('MMMM' => 'MMM', 'LLLL' => 'LLL', 'MMM' => 'MMMM', 'LLL' => 'LLLL')); diff --git a/plugins/Intl/DateTimeFormatProvider.php b/plugins/Intl/DateTimeFormatProvider.php new file mode 100644 index 0000000000..19fcd7acca --- /dev/null +++ b/plugins/Intl/DateTimeFormatProvider.php @@ -0,0 +1,133 @@ +<?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\Intl; + +use Piwik\Plugins\LanguagesManager\LanguagesManager; +use Piwik\Translation\Translator; + +/** + * Provides date and time formats. + */ +class DateTimeFormatProvider extends \Piwik\Intl\Data\Provider\DateTimeFormatProvider +{ + protected $use12HourClock; + + /** + * @var Translator + */ + protected $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + /** + * Returns the format pattern for the given format type + * + * @param int $format one of the format constants + * + * @return string + */ + public function getFormatPattern($format) + { + + switch ($format) { + case self::DATETIME_FORMAT_LONG: + $pattern = $this->translator->translate('Intl_Format_DateTime_Long'); + break; + + case self::DATETIME_FORMAT_SHORT: + $pattern = $this->translator->translate('Intl_Format_DateTime_Short'); + break; + + case self::DATE_FORMAT_LONG: + $pattern = $this->translator->translate('Intl_Format_Date_Long'); + break; + + case self::DATE_FORMAT_DAY_MONTH: + $pattern = $this->translator->translate('Intl_Format_Date_Day_Month'); + break; + + case self::DATE_FORMAT_SHORT: + $pattern = $this->translator->translate('Intl_Format_Date_Short'); + break; + + case self::DATE_FORMAT_MONTH_SHORT: + $pattern = $this->translator->translate('Intl_Format_Month_Short'); + break; + + case self::DATE_FORMAT_MONTH_LONG: + $pattern = $this->translator->translate('Intl_Format_Month_Long'); + break; + + case self::DATE_FORMAT_YEAR: + $pattern = $this->translator->translate('Intl_Format_Year'); + break; + + case self::TIME_FORMAT: + $pattern = $this->translator->translate('Intl_Format_Time'); + break; + + default: + $pattern = $format; + } + + if (strpos($pattern, '{time}') !== false) { + $pattern = str_replace('{time}', $this->getTimeFormat(), $pattern); + } + + return $pattern; + } + + /** + * Returns interval format pattern for the given format type + * + * @param bool $short whether to return short or long format pattern + * @param string $maxDifference maximal difference in interval dates (Y, M or D) + * + * @return string + */ + public function getRangeFormatPattern($short=false, $maxDifference='Y') + { + return $this->translator->translate( + sprintf( + 'Intl_Format_Interval_%s_%s', + $short ? 'Short' : 'Long', + $maxDifference + )); + } + + protected function getTimeFormat() + { + if (is_null($this->use12HourClock)) { + $this->use12HourClock = LanguagesManager::uses12HourClockForCurrentUser(); + } + + $timeFormat = 'Intl_Format_Time_24'; + + if ($this->use12HourClock) { + $timeFormat = 'Intl_Format_Time_12'; + } + + $template = $this->translator->translate($timeFormat); + + return $template; + } + + /** + * For testing purpose only: Overwrites time format + * + * @param bool $use12HourClock + */ + public function forceTimeFormat($use12HourClock = false) + { + $this->use12HourClock = $use12HourClock; + } +}
\ No newline at end of file diff --git a/plugins/Intl/config/config.php b/plugins/Intl/config/config.php new file mode 100644 index 0000000000..11e540614e --- /dev/null +++ b/plugins/Intl/config/config.php @@ -0,0 +1,5 @@ +<?php + +return array( + 'Piwik\Intl\Data\Provider\DateTimeFormatProvider' => DI\object('Piwik\Plugins\Intl\DateTimeFormatProvider') +);
\ No newline at end of file diff --git a/plugins/Intl/lang/am.json b/plugins/Intl/lang/am.json index 4999259850..e6fc689e52 100644 --- a/plugins/Intl/lang/am.json +++ b/plugins/Intl/lang/am.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "ቅዳሜ", "Day_Short_StandAlone_7": "እሑድ", "EnglishLanguageName": "Amharic", - "Format_DateTime_Long": "EEEE ፣d MMMM y h:mm:ss a", - "Format_DateTime_Short": "d MMM y h:mm:ss a", + "Format_DateTime_Long": "EEEE ፣d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E፣ MMM d", "Format_Date_Long": "EEEE ፣d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "MMM d፣ y – MMM d፣ y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ሰዓቶች", "Language_aa": "አፋርኛ", diff --git a/plugins/Intl/lang/ar.json b/plugins/Intl/lang/ar.json index 481c4e62ce..be7becbd50 100644 --- a/plugins/Intl/lang/ar.json +++ b/plugins/Intl/lang/ar.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "السبت", "Day_Short_StandAlone_7": "الأحد", "EnglishLanguageName": "Arabic", - "Format_DateTime_Long": "EEEE، d MMMM، y h:mm:ss a", - "Format_DateTime_Short": "dd\/MM\/y h:mm:ss a", + "Format_DateTime_Long": "EEEE، d MMMM، y {time}", + "Format_DateTime_Short": "dd\/MM\/y {time}", "Format_Date_Day_Month": "E، d MMM", "Format_Date_Long": "EEEE، d MMMM، y", "Format_Date_Short": "dd\/MM\/y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM، y – d MMM، y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ساعات", "Language_aa": "الأفارية", diff --git a/plugins/Intl/lang/be.json b/plugins/Intl/lang/be.json index 88bb7c14cb..c5b8f8c826 100644 --- a/plugins/Intl/lang/be.json +++ b/plugins/Intl/lang/be.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Нд", "EnglishLanguageName": "Belarusian", - "Format_DateTime_Long": "EEEE, d MMMM y HH.mm.ss", - "Format_DateTime_Short": "d.M.y HH.mm.ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d.M.y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d.M.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLL y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "hh.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "гадзіны", "Language_ab": "Абхазская", diff --git a/plugins/Intl/lang/bg.json b/plugins/Intl/lang/bg.json index 02880b589d..511e99fffd 100644 --- a/plugins/Intl/lang/bg.json +++ b/plugins/Intl/lang/bg.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Нд", "EnglishLanguageName": "Bulgarian", - "Format_DateTime_Long": "EEEE, d MMMM y 'г'. H:mm:ss", - "Format_DateTime_Short": "d.MM.y 'г'. H:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y 'г'. {time}", + "Format_DateTime_Short": "d.MM.y 'г'. {time}", "Format_Date_Day_Month": "E, d.MM", "Format_Date_Long": "EEEE, d MMMM y 'г'.", "Format_Date_Short": "d.MM.y 'г'.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d.MM.y 'г'. – d.MM.y 'г'.", "Format_Month_Long": "MMMM y 'г'.", "Format_Month_Short": "MM.y 'г'.", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y 'г'.", "Hours": "часове", "Language_aa": "Афар", diff --git a/plugins/Intl/lang/bn.json b/plugins/Intl/lang/bn.json index 2bac13f1be..92676e3ab4 100644 --- a/plugins/Intl/lang/bn.json +++ b/plugins/Intl/lang/bn.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "শনি", "Day_Short_StandAlone_7": "রবি", "EnglishLanguageName": "Bengali", - "Format_DateTime_Long": "EEEE, d MMMM, y h:mm:ss a", - "Format_DateTime_Short": "d MMM, y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM, y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE, d MMMM, y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ঘন্টা", "Language_aa": "আফার", diff --git a/plugins/Intl/lang/bs.json b/plugins/Intl/lang/bs.json index 89e7950363..abd090f32c 100644 --- a/plugins/Intl/lang/bs.json +++ b/plugins/Intl/lang/bs.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sub", "Day_Short_StandAlone_7": "Ned", "EnglishLanguageName": "Bosnian", - "Format_DateTime_Long": "EEEE, dd. MMMM y. HH:mm:ss", - "Format_DateTime_Short": "dd. MMM. y. HH:mm:ss", + "Format_DateTime_Long": "EEEE, dd. MMMM y. {time}", + "Format_DateTime_Short": "dd. MMM. y. {time}", "Format_Date_Day_Month": "E, dd. MMM", "Format_Date_Long": "EEEE, dd. MMMM y.", "Format_Date_Short": "dd. MMM. y.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd. MMM y. – dd. MMM y.", "Format_Month_Long": "LLLL y.", "Format_Month_Short": "MMM y.", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "hh:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y.", "Hours": "sati", "Language_aa": "Afarski", diff --git a/plugins/Intl/lang/ca.json b/plugins/Intl/lang/ca.json index d2b8a9a26e..7a0b18b324 100644 --- a/plugins/Intl/lang/ca.json +++ b/plugins/Intl/lang/ca.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Ds.", "Day_Short_StandAlone_7": "Dg.", "EnglishLanguageName": "Catalan", - "Format_DateTime_Long": "EEEE, d MMMM 'de' y H:mm:ss", - "Format_DateTime_Short": "d MMM y H:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM 'de' y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM 'de' y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "LLLL 'de' y", "Format_Month_Short": "LLL 'de' y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "hores", "Language_aa": "Àfar", diff --git a/plugins/Intl/lang/cs.json b/plugins/Intl/lang/cs.json index 813e0506ee..5d13f324a1 100644 --- a/plugins/Intl/lang/cs.json +++ b/plugins/Intl/lang/cs.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "So", "Day_Short_StandAlone_7": "Ne", "EnglishLanguageName": "Czech", - "Format_DateTime_Long": "EEEE d. MMMM y H:mm:ss", - "Format_DateTime_Short": "d. M. y H:mm:ss", + "Format_DateTime_Long": "EEEE d. MMMM y {time}", + "Format_DateTime_Short": "d. M. y {time}", "Format_Date_Day_Month": "E d. M.", "Format_Date_Long": "EEEE d. MMMM y", "Format_Date_Short": "d. M. y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. M. y – d. M. y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLLL y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "hodiny", "Language_aa": "Afarština", diff --git a/plugins/Intl/lang/cy.json b/plugins/Intl/lang/cy.json index 69a71a6022..eea1cb48d6 100644 --- a/plugins/Intl/lang/cy.json +++ b/plugins/Intl/lang/cy.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sad", "Day_Short_StandAlone_7": "Sul", "EnglishLanguageName": "Welsh", - "Format_DateTime_Long": "EEEE, d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "oriau", "Language_aa": "Affareg", diff --git a/plugins/Intl/lang/da.json b/plugins/Intl/lang/da.json index 3710ea0c0b..960a7be385 100644 --- a/plugins/Intl/lang/da.json +++ b/plugins/Intl/lang/da.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lør", "Day_Short_StandAlone_7": "Søn", "EnglishLanguageName": "Danish", - "Format_DateTime_Long": "EEEE 'den' d. MMMM y HH.mm.ss", - "Format_DateTime_Short": "d. MMM y HH.mm.ss", + "Format_DateTime_Long": "EEEE 'den' d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE 'den' d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "timer", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/de.json b/plugins/Intl/lang/de.json index 813e67dde7..d6a6e7e70b 100644 --- a/plugins/Intl/lang/de.json +++ b/plugins/Intl/lang/de.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sa", "Day_Short_StandAlone_7": "So", "EnglishLanguageName": "German", - "Format_DateTime_Long": "EEEE, d. MMMM y HH:mm:ss", - "Format_DateTime_Short": "dd.MM.y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "dd.MM.y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "dd.MM.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "Stunden", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/el.json b/plugins/Intl/lang/el.json index 2af13ce2f3..49c3bf9897 100644 --- a/plugins/Intl/lang/el.json +++ b/plugins/Intl/lang/el.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Σάβ", "Day_Short_StandAlone_7": "Κυρ", "EnglishLanguageName": "Greek", - "Format_DateTime_Long": "EEEE, d MMMM y h:mm:ss a", - "Format_DateTime_Short": "d MMM y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd MMM y – dd MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ώρες", "Language_aa": "Αφάρ", diff --git a/plugins/Intl/lang/en.json b/plugins/Intl/lang/en.json index a52b7b2cf6..db73bc9a08 100644 --- a/plugins/Intl/lang/en.json +++ b/plugins/Intl/lang/en.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sat", "Day_Short_StandAlone_7": "Sun", "EnglishLanguageName": "English", - "Format_DateTime_Long": "EEEE, MMMM d, y h:mm:ss a", - "Format_DateTime_Short": "MMM d, y h:mm:ss a", + "Format_DateTime_Long": "EEEE, MMMM d, y {time}", + "Format_DateTime_Short": "MMM d, y {time}", "Format_Date_Day_Month": "E, MMM d", "Format_Date_Long": "EEEE, MMMM d, y", "Format_Date_Short": "MMM d, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "MMM d, y – MMM d, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "hours", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/es.json b/plugins/Intl/lang/es.json index 10590cb926..4a08bbae01 100644 --- a/plugins/Intl/lang/es.json +++ b/plugins/Intl/lang/es.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sáb.", "Day_Short_StandAlone_7": "Dom.", "EnglishLanguageName": "Spanish", - "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y H:mm:ss", - "Format_DateTime_Short": "d MMM y H:mm:ss", + "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d 'de' MMMM 'de' y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM 'de' y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/et.json b/plugins/Intl/lang/et.json index db9277a739..bb9cdd7b16 100644 --- a/plugins/Intl/lang/et.json +++ b/plugins/Intl/lang/et.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "L", "Day_Short_StandAlone_7": "P", "EnglishLanguageName": "Estonian", - "Format_DateTime_Long": "EEEE, d. MMMM y H:mm.ss", - "Format_DateTime_Short": "d. MMM y H:mm.ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm.ss a", + "Format_Time_24": "H:mm.ss", "Format_Year": "y", "Hours": "tunnid", "Language_aa": "Afari", diff --git a/plugins/Intl/lang/eu.json b/plugins/Intl/lang/eu.json index a102605b6d..ec30c090f1 100644 --- a/plugins/Intl/lang/eu.json +++ b/plugins/Intl/lang/eu.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lr.", "Day_Short_StandAlone_7": "Ig.", "EnglishLanguageName": "Basque", - "Format_DateTime_Long": "y('e')'ko' MMMM d, EEEE HH:mm:ss", - "Format_DateTime_Short": "y MMM d HH:mm:ss", + "Format_DateTime_Long": "y('e')'ko' MMMM d, EEEE {time}", + "Format_DateTime_Short": "y MMM d {time}", "Format_Date_Day_Month": "MMM d, E", "Format_Date_Long": "y('e')'ko' MMMM d, EEEE", "Format_Date_Short": "y MMM d", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y('e')'ko' MMM d – y('e')'ko' MMM d", "Format_Month_Long": "y('e')'ko' MMMM", "Format_Month_Short": "y MMM", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ordu", "Language_ab": "Abkhazera", diff --git a/plugins/Intl/lang/fa.json b/plugins/Intl/lang/fa.json index 26110dfa0d..992ec00575 100644 --- a/plugins/Intl/lang/fa.json +++ b/plugins/Intl/lang/fa.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "شنبه", "Day_Short_StandAlone_7": "یکشنبه", "EnglishLanguageName": "Persian", - "Format_DateTime_Long": "EEEE d MMMM y H:mm:ss", - "Format_DateTime_Short": "d MMM y H:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d LLL", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y تا d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "ساعت", "Language_aa": "آفاری", diff --git a/plugins/Intl/lang/fi.json b/plugins/Intl/lang/fi.json index eb8f503320..ce88fa7bd5 100644 --- a/plugins/Intl/lang/fi.json +++ b/plugins/Intl/lang/fi.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "La", "Day_Short_StandAlone_7": "Su", "EnglishLanguageName": "Finnish", - "Format_DateTime_Long": "cccc d. MMMM y H.mm.ss", - "Format_DateTime_Short": "d.M.y H.mm.ss", + "Format_DateTime_Long": "cccc d. MMMM y {time}", + "Format_DateTime_Short": "d.M.y {time}", "Format_Date_Day_Month": "ccc d. MMM", "Format_Date_Long": "cccc d. MMMM y", "Format_Date_Short": "d.M.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMMM y – d. MMMM y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLL y", - "Format_Time": "H.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "H.mm.ss", "Format_Year": "y", "Hours": "tunnit", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/fr.json b/plugins/Intl/lang/fr.json index 57438a91f0..3b66a6e02e 100644 --- a/plugins/Intl/lang/fr.json +++ b/plugins/Intl/lang/fr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sam.", "Day_Short_StandAlone_7": "Dim.", "EnglishLanguageName": "French", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "heures", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/gl.json b/plugins/Intl/lang/gl.json index 15c88b8530..91d274e898 100644 --- a/plugins/Intl/lang/gl.json +++ b/plugins/Intl/lang/gl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sáb", "Day_Short_StandAlone_7": "Dom", "EnglishLanguageName": "Galician", - "Format_DateTime_Long": "EEEE dd MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM, y HH:mm:ss", + "Format_DateTime_Long": "EEEE dd MMMM y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE dd MMMM y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_ab": "Abkhazo", diff --git a/plugins/Intl/lang/he.json b/plugins/Intl/lang/he.json index 50c63fc1d4..78f07a3bc1 100644 --- a/plugins/Intl/lang/he.json +++ b/plugins/Intl/lang/he.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "שבת", "Day_Short_StandAlone_7": "יום א׳", "EnglishLanguageName": "Hebrew", - "Format_DateTime_Long": "EEEE, d בMMMM y H:mm:ss", - "Format_DateTime_Short": "d בMMM y H:mm:ss", + "Format_DateTime_Long": "EEEE, d בMMMM y {time}", + "Format_DateTime_Short": "d בMMM y {time}", "Format_Date_Day_Month": "E, d בMMM", "Format_Date_Long": "EEEE, d בMMMM y", "Format_Date_Short": "d בMMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "שעות", "Language_aa": "אפארית", diff --git a/plugins/Intl/lang/hi.json b/plugins/Intl/lang/hi.json index 73c68868e8..d9284fd761 100644 --- a/plugins/Intl/lang/hi.json +++ b/plugins/Intl/lang/hi.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "शनि", "Day_Short_StandAlone_7": "रवि", "EnglishLanguageName": "Hindi", - "Format_DateTime_Long": "EEEE, d MMMM y h:mm:ss a", - "Format_DateTime_Short": "dd\/MM\/y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "dd\/MM\/y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "dd\/MM\/y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "घंटे", "Language_aa": "अफ़ार", diff --git a/plugins/Intl/lang/hr.json b/plugins/Intl/lang/hr.json index 48bcdd8a09..39f7d403d9 100644 --- a/plugins/Intl/lang/hr.json +++ b/plugins/Intl/lang/hr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sub", "Day_Short_StandAlone_7": "Ned", "EnglishLanguageName": "Croatian", - "Format_DateTime_Long": "EEEE, d. MMMM y. HH:mm:ss", - "Format_DateTime_Short": "d. MMM y. HH:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y. {time}", + "Format_DateTime_Short": "d. MMM y. {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y.", "Format_Date_Short": "d. MMM y.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd. MMM y. – dd. MMM y.", "Format_Month_Long": "LLLL y.", "Format_Month_Short": "LLL y.", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "hh:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y.", "Hours": "sati", "Language_aa": "Afarski", diff --git a/plugins/Intl/lang/hu.json b/plugins/Intl/lang/hu.json index 4df06ba706..e730da6862 100644 --- a/plugins/Intl/lang/hu.json +++ b/plugins/Intl/lang/hu.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Szo", "Day_Short_StandAlone_7": "V", "EnglishLanguageName": "Hungarian", - "Format_DateTime_Long": "y. MMMM d., EEEE H:mm:ss", - "Format_DateTime_Short": "y. MMM d. H:mm:ss", + "Format_DateTime_Long": "y. MMMM d., EEEE {time}", + "Format_DateTime_Short": "y. MMM d. {time}", "Format_Date_Day_Month": "MMM d., E", "Format_Date_Long": "y. MMMM d., EEEE", "Format_Date_Short": "y. MMM d.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y. MMM d. – y. MMM d.", "Format_Month_Long": "y. MMMM", "Format_Month_Short": "y. MMM", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "H:mm:ss", "Format_Year": "y.", "Hours": "óra", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/id.json b/plugins/Intl/lang/id.json index ef6ae7fafa..2ed5145d34 100644 --- a/plugins/Intl/lang/id.json +++ b/plugins/Intl/lang/id.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sab", "Day_Short_StandAlone_7": "Min", "EnglishLanguageName": "Indonesian", - "Format_DateTime_Long": "EEEE, dd MMMM y HH.mm.ss", - "Format_DateTime_Short": "d MMM y HH.mm.ss", + "Format_DateTime_Long": "EEEE, dd MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, dd MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "jam", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/is.json b/plugins/Intl/lang/is.json index 1160564dd8..b27b8783aa 100644 --- a/plugins/Intl/lang/is.json +++ b/plugins/Intl/lang/is.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lau.", "Day_Short_StandAlone_7": "Sun.", "EnglishLanguageName": "Icelandic", - "Format_DateTime_Long": "EEEE, d. MMMM y HH:mm:ss", - "Format_DateTime_Short": "d. MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y – d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "klukkustundir", "Language_ab": "Abkasíska", diff --git a/plugins/Intl/lang/it.json b/plugins/Intl/lang/it.json index 5f88825369..a8fdaac903 100644 --- a/plugins/Intl/lang/it.json +++ b/plugins/Intl/lang/it.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sab", "Day_Short_StandAlone_7": "Dom", "EnglishLanguageName": "Italian", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "dd MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "dd MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "dd MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd MMM y – dd MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ore", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ja.json b/plugins/Intl/lang/ja.json index b1434f36d6..e63c4094d6 100644 --- a/plugins/Intl/lang/ja.json +++ b/plugins/Intl/lang/ja.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "土", "Day_Short_StandAlone_7": "日", "EnglishLanguageName": "Japanese", - "Format_DateTime_Long": "y年M月d日EEEE H:mm:ss", - "Format_DateTime_Short": "y\/MM\/dd H:mm:ss", + "Format_DateTime_Long": "y年M月d日EEEE {time}", + "Format_DateTime_Short": "y\/MM\/dd {time}", "Format_Date_Day_Month": "M月d日(E)", "Format_Date_Long": "y年M月d日EEEE", "Format_Date_Short": "y\/MM\/dd", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y年M月d日~y年M月d日", "Format_Month_Long": "y年M月", "Format_Month_Short": "y年M月", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "aK:mm:ss", + "Format_Time_24": "H:mm:ss", "Format_Year": "y年", "Hours": "時間", "Language_aa": "アファル語", diff --git a/plugins/Intl/lang/ka.json b/plugins/Intl/lang/ka.json index 819f7ba2a0..91af287421 100644 --- a/plugins/Intl/lang/ka.json +++ b/plugins/Intl/lang/ka.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "შაბ", "Day_Short_StandAlone_7": "კვი", "EnglishLanguageName": "Georgian", - "Format_DateTime_Long": "EEEE, dd MMMM, y HH:mm:ss", - "Format_DateTime_Short": "d MMM. y HH:mm:ss", + "Format_DateTime_Long": "EEEE, dd MMMM, y {time}", + "Format_DateTime_Short": "d MMM. y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, dd MMMM, y", "Format_Date_Short": "d MMM. y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd MMM. y – d MMM. y", "Format_Month_Long": "MMMM, y", "Format_Month_Short": "MMM. y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "საათი", "Language_aa": "აფარი", diff --git a/plugins/Intl/lang/ko.json b/plugins/Intl/lang/ko.json index 70b13cb503..229b1e6144 100644 --- a/plugins/Intl/lang/ko.json +++ b/plugins/Intl/lang/ko.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "토", "Day_Short_StandAlone_7": "일", "EnglishLanguageName": "Korean", - "Format_DateTime_Long": "y년 M월 d일 EEEE a h:mm:ss", - "Format_DateTime_Short": "y. M. d. a h:mm:ss", + "Format_DateTime_Long": "y년 M월 d일 EEEE {time}", + "Format_DateTime_Short": "y. M. d. {time}", "Format_Date_Day_Month": "MMM d일 (E)", "Format_Date_Long": "y년 M월 d일 EEEE", "Format_Date_Short": "y. M. d.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y년 M월 d일 ~ y년 M월 d일", "Format_Month_Long": "y년 MMMM", "Format_Month_Short": "y년 MMM", - "Format_Time": "a h:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "H시 m분 s초", "Format_Year": "y년", "Hours": "시간", "Language_aa": "아파르어", diff --git a/plugins/Intl/lang/lt.json b/plugins/Intl/lang/lt.json index 7dc87c76a1..2fefcf67dc 100644 --- a/plugins/Intl/lang/lt.json +++ b/plugins/Intl/lang/lt.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Št", "Day_Short_StandAlone_7": "Sk", "EnglishLanguageName": "Lithuanian", - "Format_DateTime_Long": "y 'm'. MMMM d 'd'., EEEE HH:mm:ss", - "Format_DateTime_Short": "y-MM-dd HH:mm:ss", + "Format_DateTime_Long": "y 'm'. MMMM d 'd'., EEEE {time}", + "Format_DateTime_Short": "y-MM-dd {time}", "Format_Date_Day_Month": "MM-dd, E", "Format_Date_Long": "y 'm'. MMMM d 'd'., EEEE", "Format_Date_Short": "y-MM-dd", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y MMM d – y MMM d", "Format_Month_Long": "y 'm'. LLLL", "Format_Month_Short": "y-MM", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "hh:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "valandos", "Language_aa": "Afarų", diff --git a/plugins/Intl/lang/lv.json b/plugins/Intl/lang/lv.json index 2a91a4b6fb..583d3e51a0 100644 --- a/plugins/Intl/lang/lv.json +++ b/plugins/Intl/lang/lv.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Se", "Day_Short_StandAlone_7": "Sv", "EnglishLanguageName": "Latvian", - "Format_DateTime_Long": "EEEE, y. 'gada' d. MMMM HH:mm:ss", - "Format_DateTime_Short": "y. 'gada' d. MMM HH:mm:ss", + "Format_DateTime_Long": "EEEE, y. 'gada' d. MMMM {time}", + "Format_DateTime_Short": "y. 'gada' d. MMM {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, y. 'gada' d. MMMM", "Format_Date_Short": "y. 'gada' d. MMM", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y. 'gada' d. MMM – y. 'gada' d. MMM", "Format_Month_Long": "y. 'g'. MMMM", "Format_Month_Short": "y. 'g'. MMM", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y. 'g'.", "Hours": "stundas", "Language_aa": "Afāru", diff --git a/plugins/Intl/lang/nb.json b/plugins/Intl/lang/nb.json index 84d427bd0b..812c461829 100644 --- a/plugins/Intl/lang/nb.json +++ b/plugins/Intl/lang/nb.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lør.", "Day_Short_StandAlone_7": "Søn.", "EnglishLanguageName": "Norwegian Bokmål", - "Format_DateTime_Long": "EEEE d. MMMM y HH.mm.ss", - "Format_DateTime_Short": "d. MMM y HH.mm.ss", + "Format_DateTime_Long": "EEEE d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y–d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "h.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y", "Hours": "timer", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/nl.json b/plugins/Intl/lang/nl.json index 8c9cfa5953..66398aa589 100644 --- a/plugins/Intl/lang/nl.json +++ b/plugins/Intl/lang/nl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Za", "Day_Short_StandAlone_7": "Zo", "EnglishLanguageName": "Dutch", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "uur", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/nn.json b/plugins/Intl/lang/nn.json index 6f913f08ea..cbbc0bfdda 100644 --- a/plugins/Intl/lang/nn.json +++ b/plugins/Intl/lang/nn.json @@ -296,8 +296,8 @@ "Day_Short_StandAlone_6": "La.", "Day_Short_StandAlone_7": "Sø.", "EnglishLanguageName": "Norwegian Nynorsk", - "Format_DateTime_Long": "EEEE d. MMMM y HH:mm:ss", - "Format_DateTime_Short": "d. MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE d. MMMM y", "Format_Date_Short": "d. MMM y", @@ -309,7 +309,9 @@ "Format_Interval_Short_Y": "d. MMM y–d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "hr", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/pl.json b/plugins/Intl/lang/pl.json index 7183cd5cbc..f190ed6d4d 100644 --- a/plugins/Intl/lang/pl.json +++ b/plugins/Intl/lang/pl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sob.", "Day_Short_StandAlone_7": "Niedz.", "EnglishLanguageName": "Polish", - "Format_DateTime_Long": "EEEE, d MMMM y HH:mm:ss", - "Format_DateTime_Short": "dd.MM.y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "dd.MM.y {time}", "Format_Date_Day_Month": "E, d.MM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "dd.MM.y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd.MM.y–dd.MM.y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "MM.y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "godziny", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/pt-br.json b/plugins/Intl/lang/pt-br.json index 4e934e942d..9f2a028bb4 100644 --- a/plugins/Intl/lang/pt-br.json +++ b/plugins/Intl/lang/pt-br.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sáb", "Day_Short_StandAlone_7": "Dom", "EnglishLanguageName": "Brazilian Portuguese", - "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y HH:mm:ss", - "Format_DateTime_Short": "d 'de' MMM 'de' y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y {time}", + "Format_DateTime_Short": "d 'de' MMM 'de' y {time}", "Format_Date_Day_Month": "E, d 'de' MMM", "Format_Date_Long": "EEEE, d 'de' MMMM 'de' y", "Format_Date_Short": "d 'de' MMM 'de' y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d 'de' MMM 'de' y – d 'de' MMM 'de' y", "Format_Month_Long": "MMMM 'de' y", "Format_Month_Short": "MMM 'de' y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/pt.json b/plugins/Intl/lang/pt.json index bacafa576d..60270770df 100644 --- a/plugins/Intl/lang/pt.json +++ b/plugins/Intl/lang/pt.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sábado", "Day_Short_StandAlone_7": "Domingo", "EnglishLanguageName": "Portuguese", - "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y HH:mm:ss", - "Format_DateTime_Short": "dd\/MM\/y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d 'de' MMMM 'de' y {time}", + "Format_DateTime_Short": "dd\/MM\/y {time}", "Format_Date_Day_Month": "E, d\/MM", "Format_Date_Long": "EEEE, d 'de' MMMM 'de' y", "Format_Date_Short": "dd\/MM\/y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d 'de' MMM 'de' y – d 'de' MMM 'de' y", "Format_Month_Long": "MMMM 'de' y", "Format_Month_Short": "MM\/y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "horas", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ro.json b/plugins/Intl/lang/ro.json index 2e8973de30..8f5b576ffb 100644 --- a/plugins/Intl/lang/ro.json +++ b/plugins/Intl/lang/ro.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sâm.", "Day_Short_StandAlone_7": "Dum.", "EnglishLanguageName": "Romanian", - "Format_DateTime_Long": "EEEE, d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ore", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ru.json b/plugins/Intl/lang/ru.json index b26d882971..593daf525d 100644 --- a/plugins/Intl/lang/ru.json +++ b/plugins/Intl/lang/ru.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Вс", "EnglishLanguageName": "Russian", - "Format_DateTime_Long": "EEEE, d MMMM y 'г'. H:mm:ss", - "Format_DateTime_Short": "d MMM y 'г'. H:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y 'г'. {time}", + "Format_DateTime_Short": "d MMM y 'г'. {time}", "Format_Date_Day_Month": "ccc, d MMM", "Format_Date_Long": "EEEE, d MMMM y 'г'.", "Format_Date_Short": "d MMM y 'г'.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y 'г'.", "Format_Month_Long": "LLLL y 'г'.", "Format_Month_Short": "LLL y 'г'.", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "часы", "Language_aa": "Афар", diff --git a/plugins/Intl/lang/sk.json b/plugins/Intl/lang/sk.json index d926c8f1bd..db39d8d374 100644 --- a/plugins/Intl/lang/sk.json +++ b/plugins/Intl/lang/sk.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "So", "Day_Short_StandAlone_7": "Ne", "EnglishLanguageName": "Slovak", - "Format_DateTime_Long": "EEEE, d. MMMM y H:mm:ss", - "Format_DateTime_Short": "d. M. y H:mm:ss", + "Format_DateTime_Long": "EEEE, d. MMMM y {time}", + "Format_DateTime_Short": "d. M. y {time}", "Format_Date_Day_Month": "E d. M.", "Format_Date_Long": "EEEE, d. MMMM y", "Format_Date_Short": "d. M. y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. M. y – d. M. y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "M\/y", - "Format_Time": "H:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "H:mm:ss", "Format_Year": "y", "Hours": "hodiny", "Language_aa": "Afarčina", diff --git a/plugins/Intl/lang/sl.json b/plugins/Intl/lang/sl.json index 054f53ca8b..2610cb1fa1 100644 --- a/plugins/Intl/lang/sl.json +++ b/plugins/Intl/lang/sl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sob", "Day_Short_StandAlone_7": "Ned", "EnglishLanguageName": "Slovenian", - "Format_DateTime_Long": "EEEE, dd. MMMM y HH:mm:ss", - "Format_DateTime_Short": "d. MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE, dd. MMMM y {time}", + "Format_DateTime_Short": "d. MMM y {time}", "Format_Date_Day_Month": "E, d. MMM", "Format_Date_Long": "EEEE, dd. MMMM y", "Format_Date_Short": "d. MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d. MMM y–d. MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ur", "Language_aa": "Afarščina", diff --git a/plugins/Intl/lang/sq.json b/plugins/Intl/lang/sq.json index ee9aa0eae3..1c9acf73d3 100644 --- a/plugins/Intl/lang/sq.json +++ b/plugins/Intl/lang/sq.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sht", "Day_Short_StandAlone_7": "Die", "EnglishLanguageName": "Albanian", - "Format_DateTime_Long": "EEEE, d MMMM y h:mm:ss a", - "Format_DateTime_Short": "d MMM y h:mm:ss a", + "Format_DateTime_Long": "EEEE, d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "orë", "Language_ab": "Abkazisht", diff --git a/plugins/Intl/lang/sr.json b/plugins/Intl/lang/sr.json index 003e91483c..3e59d08f33 100644 --- a/plugins/Intl/lang/sr.json +++ b/plugins/Intl/lang/sr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Суб", "Day_Short_StandAlone_7": "Нед", "EnglishLanguageName": "Serbian", - "Format_DateTime_Long": "EEEE, dd. MMMM y. HH.mm.ss", - "Format_DateTime_Short": "dd.MM.y. HH.mm.ss", + "Format_DateTime_Long": "EEEE, dd. MMMM y. {time}", + "Format_DateTime_Short": "dd.MM.y. {time}", "Format_Date_Day_Month": "E d. MMM", "Format_Date_Long": "EEEE, dd. MMMM y.", "Format_Date_Short": "dd.MM.y.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "dd. MMM y. – dd. MMM y.", "Format_Month_Long": "MMMM y.", "Format_Month_Short": "MMM y.", - "Format_Time": "HH.mm.ss", + "Format_Time": "{time}", + "Format_Time_12": "hh.mm.ss a", + "Format_Time_24": "HH.mm.ss", "Format_Year": "y.", "Hours": "сати", "Language_aa": "Афарски", diff --git a/plugins/Intl/lang/sv.json b/plugins/Intl/lang/sv.json index c829d88418..6652fd399b 100644 --- a/plugins/Intl/lang/sv.json +++ b/plugins/Intl/lang/sv.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Lör", "Day_Short_StandAlone_7": "Sön", "EnglishLanguageName": "Swedish", - "Format_DateTime_Long": "EEEE d MMMM y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEE d MMMM y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEE d MMMM y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y–d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "timmar", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/ta.json b/plugins/Intl/lang/ta.json index 43708c2ba9..a24fad18e1 100644 --- a/plugins/Intl/lang/ta.json +++ b/plugins/Intl/lang/ta.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "சனி", "Day_Short_StandAlone_7": "ஞாயி.", "EnglishLanguageName": "Tamil", - "Format_DateTime_Long": "EEEE, d MMMM, y a h:mm:ss", - "Format_DateTime_Short": "d MMM, y a h:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM, y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "MMM d, E", "Format_Date_Long": "EEEE, d MMMM, y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "a h:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "மணிநேரங்கள்", "Language_aa": "அஃபார்", diff --git a/plugins/Intl/lang/te.json b/plugins/Intl/lang/te.json index 7c9679b3a1..9122d011da 100644 --- a/plugins/Intl/lang/te.json +++ b/plugins/Intl/lang/te.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "శని", "Day_Short_StandAlone_7": "ఆది", "EnglishLanguageName": "Telugu", - "Format_DateTime_Long": "d, MMMM y, EEEE h:mm:ss a", - "Format_DateTime_Short": "d MMM, y h:mm:ss a", + "Format_DateTime_Long": "d, MMMM y, EEEE {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "d, MMMM y, EEEE", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM, y – d MMM, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "గంటలు", "Language_aa": "అఫార్", diff --git a/plugins/Intl/lang/th.json b/plugins/Intl/lang/th.json index afe4e21c49..52034808c2 100644 --- a/plugins/Intl/lang/th.json +++ b/plugins/Intl/lang/th.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "ส.", "Day_Short_StandAlone_7": "อา.", "EnglishLanguageName": "Thai", - "Format_DateTime_Long": "EEEEที่ d MMMM G y HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "EEEEที่ d MMMM G y {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "E d MMM", "Format_Date_Long": "EEEEที่ d MMMM G y", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM G y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "ชั่วโมง", "Language_aa": "อะฟาร์", diff --git a/plugins/Intl/lang/tl.json b/plugins/Intl/lang/tl.json index caebf74edf..793664da58 100644 --- a/plugins/Intl/lang/tl.json +++ b/plugins/Intl/lang/tl.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Sab", "Day_Short_StandAlone_7": "Lin", "EnglishLanguageName": "Tagalog", - "Format_DateTime_Long": "EEEE, MMMM d, y h:mm:ss a", - "Format_DateTime_Short": "MMM d, y h:mm:ss a", + "Format_DateTime_Long": "EEEE, MMMM d, y {time}", + "Format_DateTime_Short": "MMM d, y {time}", "Format_Date_Day_Month": "E, MMM d", "Format_Date_Long": "EEEE, MMMM d, y", "Format_Date_Short": "MMM d, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "MMM d, y – MMM d, y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "h:mm:ss a", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "mga oras", "Language_ab": "Abkhazian", diff --git a/plugins/Intl/lang/tr.json b/plugins/Intl/lang/tr.json index 0d2b4082e6..3ceb15c281 100644 --- a/plugins/Intl/lang/tr.json +++ b/plugins/Intl/lang/tr.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Cmt", "Day_Short_StandAlone_7": "Paz", "EnglishLanguageName": "Turkish", - "Format_DateTime_Long": "d MMMM y EEEE HH:mm:ss", - "Format_DateTime_Short": "d MMM y HH:mm:ss", + "Format_DateTime_Long": "d MMMM y EEEE {time}", + "Format_DateTime_Short": "d MMM y {time}", "Format_Date_Day_Month": "d MMMM E", "Format_Date_Long": "d MMMM y EEEE", "Format_Date_Short": "d MMM y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "MMMM y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "a h:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "saat", "Language_aa": "Afar", diff --git a/plugins/Intl/lang/uk.json b/plugins/Intl/lang/uk.json index 69ec802cbb..44d6c12e92 100644 --- a/plugins/Intl/lang/uk.json +++ b/plugins/Intl/lang/uk.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Сб", "Day_Short_StandAlone_7": "Нд", "EnglishLanguageName": "Ukrainian", - "Format_DateTime_Long": "EEEE, d MMMM y 'р'. HH:mm:ss", - "Format_DateTime_Short": "d MMM y 'р'. HH:mm:ss", + "Format_DateTime_Long": "EEEE, d MMMM y 'р'. {time}", + "Format_DateTime_Short": "d MMM y 'р'. {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, d MMMM y 'р'.", "Format_Date_Short": "d MMM y 'р'.", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "d MMM y – d MMM y", "Format_Month_Long": "LLLL y", "Format_Month_Short": "LLL y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "години", "Language_aa": "Афарська", diff --git a/plugins/Intl/lang/vi.json b/plugins/Intl/lang/vi.json index e89f2f527b..9da64b533a 100644 --- a/plugins/Intl/lang/vi.json +++ b/plugins/Intl/lang/vi.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "Th 7", "Day_Short_StandAlone_7": "CN", "EnglishLanguageName": "Vietnamese", - "Format_DateTime_Long": "EEEE, 'ngày' dd MMMM 'năm' y HH:mm:ss", - "Format_DateTime_Short": "d MMM, y HH:mm:ss", + "Format_DateTime_Long": "EEEE, 'ngày' dd MMMM 'năm' y {time}", + "Format_DateTime_Short": "d MMM, y {time}", "Format_Date_Day_Month": "E, d MMM", "Format_Date_Long": "EEEE, 'ngày' dd MMMM 'năm' y", "Format_Date_Short": "d MMM, y", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "'Ngày' dd 'tháng' M 'năm' y - 'Ngày' dd 'tháng' M 'năm' y", "Format_Month_Long": "MMMM 'năm' y", "Format_Month_Short": "MMM y", - "Format_Time": "HH:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "h:mm:ss a", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y", "Hours": "giờ", "Language_aa": "Tiếng Afar", diff --git a/plugins/Intl/lang/zh-cn.json b/plugins/Intl/lang/zh-cn.json index 5b48c6c6d8..5cd4c2c680 100644 --- a/plugins/Intl/lang/zh-cn.json +++ b/plugins/Intl/lang/zh-cn.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "周六", "Day_Short_StandAlone_7": "周日", "EnglishLanguageName": "Simplified Chinese", - "Format_DateTime_Long": "y年M月d日EEEE ah:mm:ss", - "Format_DateTime_Short": "y年M月d日 ah:mm:ss", + "Format_DateTime_Long": "y年M月d日EEEE {time}", + "Format_DateTime_Short": "y年M月d日 {time}", "Format_Date_Day_Month": "M月d日E", "Format_Date_Long": "y年M月d日EEEE", "Format_Date_Short": "y年M月d日", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y年M月d日至y年M月d日", "Format_Month_Long": "y年M月", "Format_Month_Short": "y年M月", - "Format_Time": "ah:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "ah:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y年", "Hours": "小时", "Language_aa": "阿法文", diff --git a/plugins/Intl/lang/zh-tw.json b/plugins/Intl/lang/zh-tw.json index 5fac85b0ec..1bc630d29a 100644 --- a/plugins/Intl/lang/zh-tw.json +++ b/plugins/Intl/lang/zh-tw.json @@ -300,8 +300,8 @@ "Day_Short_StandAlone_6": "週六", "Day_Short_StandAlone_7": "週日", "EnglishLanguageName": "Traditional Chinese", - "Format_DateTime_Long": "y年M月d日 EEEE ah:mm:ss", - "Format_DateTime_Short": "y年M月d日 ah:mm:ss", + "Format_DateTime_Long": "y年M月d日 EEEE {time}", + "Format_DateTime_Short": "y年M月d日 {time}", "Format_Date_Day_Month": "M月d日 E", "Format_Date_Long": "y年M月d日 EEEE", "Format_Date_Short": "y年M月d日", @@ -313,7 +313,9 @@ "Format_Interval_Short_Y": "y年M月d日至y年M月d日", "Format_Month_Long": "y年M月", "Format_Month_Short": "y年M月", - "Format_Time": "ah:mm:ss", + "Format_Time": "{time}", + "Format_Time_12": "ah:mm:ss", + "Format_Time_24": "HH:mm:ss", "Format_Year": "y年", "Hours": "小時", "Language_aa": "阿法文", diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php index dc252abbda..9d75f62c2f 100644 --- a/plugins/LanguagesManager/API.php +++ b/plugins/LanguagesManager/API.php @@ -275,6 +275,45 @@ class API extends \Piwik\Plugin\API return true; } + /** + * Returns whether the user uses 12 hour clock + * + * @param string $login + * @return string + */ + public function uses12HourClockForUser($login) + { + if ($login == 'anonymous') { + return false; + } + + Piwik::checkUserHasSuperUserAccessOrIsTheUser($login); + + $lang = $this->getModel()->uses12HourClock($login); + + return $lang; + } + + /** + * Returns whether the user uses 12 hour clock + * + * @param string $login + * @param bool $use12HourClock + * @return string + */ + public function set12HourClockForUser($login, $use12HourClock) + { + if ($login == 'anonymous') { + return false; + } + + Piwik::checkUserHasSuperUserAccessOrIsTheUser($login); + + $lang = $this->getModel()->set12HourClock($login, $use12HourClock); + + return $lang; + } + private function loadAvailableLanguages() { if (!is_null($this->availableLanguageNames)) { diff --git a/plugins/LanguagesManager/LanguagesManager.php b/plugins/LanguagesManager/LanguagesManager.php index 012de9cd50..2a468d550a 100644 --- a/plugins/LanguagesManager/LanguagesManager.php +++ b/plugins/LanguagesManager/LanguagesManager.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\LanguagesManager; use Exception; +use Piwik\API\Request; use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; @@ -138,6 +139,19 @@ class LanguagesManager extends \Piwik\Plugin } /** + * @return boolean + */ + public static function uses12HourClockForCurrentUser() + { + try { + $currentUser = Piwik::getCurrentUserLogin(); + return Request::processRequest('LanguagesManager.uses12HourClockForUser', array('login' => $currentUser)); + } catch (Exception $e) { + return false; + } + } + + /** * @return string Two letters language code, eg. "fr" */ public static function getLanguageCodeForCurrentUser() diff --git a/plugins/LanguagesManager/Model.php b/plugins/LanguagesManager/Model.php index e40452c134..fad84238cd 100644 --- a/plugins/LanguagesManager/Model.php +++ b/plugins/LanguagesManager/Model.php @@ -57,10 +57,41 @@ class Model return true; } + /** + * Returns whether the given user has choosen to use 12 hour clock + * + * @param $userLogin + * @return bool + * @throws \Exception + */ + public function uses12HourClock($userLogin) + { + return (bool) Db::fetchOne('SELECT use_12_hour_clock FROM ' . $this->table . + ' WHERE login = ? ', array($userLogin)); + } + + /** + * Sets whether the given user wants to use 12 hout clock + * + * @param string $login + * @param string $use12HourClock + * @return bool + */ + public function set12HourClock($login, $use12HourClock) + { + $query = 'INSERT INTO ' . $this->table . + ' (login, use_12_hour_clock) VALUES (?,?) ON DUPLICATE KEY UPDATE use_12_hour_clock=?'; + $bind = array($login, $use12HourClock, $use12HourClock); + Db::query($query, $bind); + + return true; + } + public static function install() { $userLanguage = "login VARCHAR( 100 ) NOT NULL , language VARCHAR( 10 ) NOT NULL , + use_12_hour_clock TINYINT(1) NOT NULL DEFAULT 0 , PRIMARY KEY ( login )"; DbHelper::createTable(self::$rawPrefix, $userLanguage); } diff --git a/plugins/LanguagesManager/Test/Integration/ModelTest.php b/plugins/LanguagesManager/Test/Integration/ModelTest.php new file mode 100644 index 0000000000..023b43f293 --- /dev/null +++ b/plugins/LanguagesManager/Test/Integration/ModelTest.php @@ -0,0 +1,136 @@ +<?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\LanguagesManager\tests\Integration; + +use Piwik\Common; +use Piwik\Db; +use Piwik\Plugins\LanguagesManager\Model; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group LanguagesManager + * @group ModelTest + * @group Plugins + */ +class ModelTest extends IntegrationTestCase +{ + + /** + * @var Model + */ + protected $model; + + public function setUp() + { + $this->model = new Model(); + parent::setUp(); + } + + public function test_install_ShouldNotFailAndActuallyCreateTheDatabases() + { + $this->assertContainTables(array('user_language')); + + $columns = Db::fetchAll('show columns from ' . Common::prefixTable('user_language')); + $this->assertCount(3, $columns); + } + + public function test_uninstall_ShouldNotFailAndRemovesAllAlertTables() + { + Model::uninstall(); + + $this->assertNotContainTables(array('user_language')); + + Model::install(); + } + + public function test_handlesUserLanguageEntriesCorrectly() + { + $this->model->setLanguageForUser('admin', 'de'); + + $this->assertTableEntryCount(1); + + $this->assertEquals('de', $this->model->getLanguageForUser('admin')); + + $this->model->deleteUserLanguage('admin'); + + $this->assertTableEntryCount(0); + } + + public function test_handlesUserTimeFormatEntriesCorrectly() + { + $this->model->set12HourClock('admin', false); + + $this->assertTableEntryCount(1); + + $this->assertEquals(false, $this->model->uses12HourClock('admin')); + + $this->model->deleteUserLanguage('admin'); + + $this->assertTableEntryCount(0); + } + + public function test_handlesUserLanguageAndTimeFormatEntriesCorrectly() + { + $this->model->setLanguageForUser('admin', 'de'); + + $this->assertTableEntryCount(1); + + $this->model->set12HourClock('admin', false); + $this->model->set12HourClock('user', true); + + $this->assertTableEntryCount(2); + + $this->assertEquals('de', $this->model->getLanguageForUser('admin')); + $this->assertEquals('', $this->model->getLanguageForUser('user')); + $this->assertEquals(false, $this->model->uses12HourClock('admin')); + $this->assertEquals(true, $this->model->uses12HourClock('user')); + + $this->model->deleteUserLanguage('admin'); + + $this->assertTableEntryCount(1); + } + + private function assertTableEntryCount($count) + { + $entryCount = Db::fetchOne('SELECT COUNT(*) FROM ' . Common::prefixTable('user_language')); + + $this->assertEquals($count, $entryCount); + + } + + private function assertContainTables($expectedTables) + { + $tableNames = $this->getCurrentAvailableTableNames(); + + foreach ($expectedTables as $expectedTable) { + $this->assertContains(Common::prefixTable($expectedTable), $tableNames); + } + } + + private function assertNotContainTables($expectedTables) + { + $tableNames = $this->getCurrentAvailableTableNames(); + + foreach ($expectedTables as $expectedTable) { + $this->assertNotContains(Common::prefixTable($expectedTable), $tableNames); + } + } + + private function getCurrentAvailableTableNames() + { + $tables = Db::fetchAll('show tables'); + + $tableNames = array(); + foreach ($tables as $table) { + $tableNames[] = array_shift($table); + } + + return $tableNames; + } +} diff --git a/plugins/LanguagesManager/Updates/2.15.1-b1.php b/plugins/LanguagesManager/Updates/2.15.1-b1.php new file mode 100644 index 0000000000..3465ad7b57 --- /dev/null +++ b/plugins/LanguagesManager/Updates/2.15.1-b1.php @@ -0,0 +1,32 @@ +<?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\LanguagesManager; + +use Piwik\Common; +use Piwik\Updater; +use Piwik\Updates; + + +class Updates_2_15_1_b1 extends Updates +{ + public function getMigrationQueries(Updater $updater) + { + $updateSql = array( + 'ALTER TABLE `' . Common::prefixTable('user_language') + . '` ADD COLUMN `use_12_hour_clock` TINYINT(1) NOT NULL DEFAULT 0 AFTER `language`' => array(1060) + ); + return $updateSql; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrationQueries(__FILE__, $this->getMigrationQueries($updater)); + } +} diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php index 93e8d67881..1a71aa7b83 100644 --- a/plugins/Live/Live.php +++ b/plugins/Live/Live.php @@ -54,5 +54,7 @@ class Live extends \Piwik\Plugin $translationKeys[] = "Live_RowActionTooltipDefault"; $translationKeys[] = "Live_RowActionTooltipWithDimension"; $translationKeys[] = "Live_SegmentedVisitorLogTitle"; + $translationKeys[] = "General_Segment"; + $translationKeys[] = "General_And"; } }
\ No newline at end of file diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php index 6d679a8a05..fa7be4dd63 100644 --- a/plugins/Live/Visitor.php +++ b/plugins/Live/Visitor.php @@ -286,7 +286,7 @@ class Visitor implements VisitorInterface unset($actionDetails[$actionIdx]); continue; - } elseif ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { + } elseif ($actionDetail['type'] == Action::TYPE_EVENT) { // Handle Event if (strlen($actionDetail['pageTitle']) > 0) { $actionDetail['eventName'] = $actionDetail['pageTitle']; @@ -301,7 +301,7 @@ class Visitor implements VisitorInterface } // Event value / Generation time - if ($actionDetail['type'] == Action::TYPE_EVENT_CATEGORY) { + if ($actionDetail['type'] == Action::TYPE_EVENT) { if (strlen($actionDetail['custom_float']) > 0) { $actionDetail['eventValue'] = round($actionDetail['custom_float'], self::EVENT_VALUE_PRECISION); } @@ -310,7 +310,7 @@ class Visitor implements VisitorInterface } unset($actionDetail['custom_float']); - if ($actionDetail['type'] != Action::TYPE_EVENT_CATEGORY) { + if ($actionDetail['type'] != Action::TYPE_EVENT) { unset($actionDetail['eventCategory']); unset($actionDetail['eventAction']); } @@ -423,7 +423,7 @@ class Visitor implements VisitorInterface $details['type'] = 'search'; $details['icon'] = 'plugins/Morpheus/images/search_ico.png'; break; - case Action::TYPE_EVENT_CATEGORY: + case Action::TYPE_EVENT: $details['type'] = 'event'; $details['icon'] = 'plugins/Morpheus/images/event.png'; break; diff --git a/plugins/Live/javascripts/rowaction.js b/plugins/Live/javascripts/rowaction.js index 8baba18a2d..5c54e961b5 100644 --- a/plugins/Live/javascripts/rowaction.js +++ b/plugins/Live/javascripts/rowaction.js @@ -150,6 +150,12 @@ var segmentName = getDimensionFromApiMethod(apiMethod); var segmentValue = findTitleOfRowHavingRawSegmentValue(apiMethod, segment); + if (!segmentName || (segment && segment.indexOf(';') > 0)) { + segmentName = _pk_translate('General_Segment'); + var segmentParts = segment.split(';'); + segmentValue = segmentParts.join(' ' + _pk_translate('General_And') + ' '); + } + segmentName = piwikHelper.escape(segmentName); segmentName = piwikHelper.htmlEntities(segmentName); segmentValue = piwikHelper.escape(segmentValue); diff --git a/plugins/Live/lang/cs.json b/plugins/Live/lang/cs.json index ec3ea624fd..caf56e0adc 100644 --- a/plugins/Live/lang/cs.json +++ b/plugins/Live/lang/cs.json @@ -4,7 +4,7 @@ "AveragePageGenerationTime": "Pro tohoto návštěvníka se každá stránka načetla v průměru za %1$s.", "CalculatedOverNPageViews": "Vypočítáno na základě %1$s posledních zobrazení stránek tohoto návštěvníka.", "ClickToViewMoreAboutVisit": "Klikněte pro zobrazení více informací o této návštěvě", - "ConvertedNGoals": "Zkonvertováno %s cílů", + "ConvertedNGoals": "Proměněno %s cílů", "EcommerceSummaryConversions": "%1$s%2$s objednávek celkem za %3$s%4$s, zakoupeno %5$s položek.", "FirstVisit": "První návštěva", "GoalType": "Typ", diff --git a/plugins/Live/lang/fr.json b/plugins/Live/lang/fr.json index 0b099319fd..ad9dd10a44 100644 --- a/plugins/Live/lang/fr.json +++ b/plugins/Live/lang/fr.json @@ -9,7 +9,7 @@ "FirstVisit": "Première visite", "GoalType": "Type", "HideMap": "Cacher la carte", - "KeywordRankedOnSearchResultForThisVisitor": "Le mot-clé %1$s a été a été noté %2$s dans la page de résultats de recherche %3$s pour ce visiteur", + "KeywordRankedOnSearchResultForThisVisitor": "Le mot-clé %1$s a été noté %2$s dans la page de résultats de recherche %3$s pour ce visiteur", "LastHours": "Dernières %s heures", "LastMinutes": "Dernières %s minutes", "LastVisit": "Dernière visite", diff --git a/plugins/Live/lang/ja.json b/plugins/Live/lang/ja.json index eee0e823c0..1bcc483702 100644 --- a/plugins/Live/lang/ja.json +++ b/plugins/Live/lang/ja.json @@ -35,6 +35,12 @@ "VisitorsInRealTime": "リアルタイムのビジター", "VisitorsLastVisit": "このビジターの最新のビジットは %s 日前です。", "VisitsFrom": "から %1$s%2$s が %3$s を訪問", - "VisitSummary": "ウェブサイト %3$s で、合計 %1$s%2$s を消費し、%4$s は %6$s 訪問で、%5$s ページ閲覧しました。%7$s" + "VisitSummary": "ウェブサイト %3$s で、合計 %1$s%2$s を消費し、%4$s は %6$s 訪問で、%5$s ページ閲覧しました。%7$s", + "RowActionTooltipDefault": "この行で分割されたビジターログを表示", + "RowActionTooltipWithDimension": "この %s で分割されたビジターログを表示", + "RowActionTooltipTitle": "セグメントビジターログを表示", + "SegmentedVisitorLogTitle": "%s が \" %s \" のビジットを示すビジターログ", + "OnClickPause": "%s が開始されました。クリックして一時停止します。", + "OnClickStart": "%s は停止しています。クリックして開始します。" } }
\ No newline at end of file diff --git a/plugins/Live/lang/ko.json b/plugins/Live/lang/ko.json index e6c78b24d0..e1de77a61f 100644 --- a/plugins/Live/lang/ko.json +++ b/plugins/Live/lang/ko.json @@ -1,16 +1,34 @@ { "Live": { + "CalculatedOverNPageViews": "해당 방문자의 %1$s 번 페이지 방문을 통해 계산되었습니다.", + "ClickToViewMoreAboutVisit": "이 방문에 대해 더 자세한 정보를 보기 위해서 클릭하세요.", + "FirstVisit": "첫 방문", "GoalType": "유형", + "HideMap": "지도 숨기기", "KeywordRankedOnSearchResultForThisVisitor": "키워드 %1$s 이 방문자의 %3$s 검색 결과 페이지에서 %2$s에 랭크되었습니다.", "LastHours": "최근 %s시간", "LastMinutes": "최근 %s분", + "LastVisit": "최근 방문", "LinkVisitorLog": "방문자 기록 자세히 보기", + "LoadMoreVisits": "더 많은 방문들 불러오기", "MorePagesNotDisplayed": "이 방문객의 표시는 더이상 없습니다", + "NbVisitor": "1명 방문자", + "NbVisitors": "%s 방문자들", + "NextVisitor": "다음 방문자", + "NoMoreVisits": "이 방문자가 방문한 페이지는 더 이상 없습니다.", "PageRefreshed": "이 페이지를 볼 수 있음 \/ 업데이트된 횟수", + "PreviousVisitor": "이전 방문자", + "RealTimeVisitorCount": "실시간 방문자 합계", "Referrer_URL": "참조 URL", + "ShowMap": "지도 보이기", + "ViewVisitorProfile": "방문자 프로필 보기", + "VisitedPages": "방문한 페이지들", "VisitorLog": "방문자 기록", "VisitorLogDocumentation": "이 표는 선택한 날짜 기간 내에 최신 방문에 표시하고 있습니다. 방문 날짜 위로 마우스를 이동하고, 그 방문자의 마지막 방문이 며칠 전인지 볼 수 있습니다. %s 기간내에 오늘이 포함되어 있으면, 방문객을 실시간으로 볼 수 있습니다! %s 여기에 표시되는 것은 보관을위한 cron 작업 설정 빈도에 상관없는 라이브 데이터입니다.", + "VisitorProfile": "방문자 프로필", "VisitorsInRealTime": "실시간 방문자", - "VisitorsLastVisit": "이 방문객의 최신 방문은 %s일 입니다." + "VisitorsLastVisit": "이 방문객의 최신 방문은 %s일 입니다.", + "OnClickPause": "%s이\/가 시작되었습니다. 멈추기 위해서 클릭해주세요.", + "OnClickStart": "%s이\/가 멈추었습니다. 시작하기 위해서 클릭해주세요." } }
\ No newline at end of file diff --git a/plugins/LogViewer b/plugins/LogViewer -Subproject 25df816eed9b0d44ebcdb89f1dfc9dde8cb3de2 +Subproject 0ee846df5038e82a87abb98e35b4d7cd1718d22 diff --git a/plugins/Login/lang/cs.json b/plugins/Login/lang/cs.json index 311341374e..8dc2f4f2f0 100644 --- a/plugins/Login/lang/cs.json +++ b/plugins/Login/lang/cs.json @@ -1,15 +1,15 @@ { "Login": { - "ConfirmationLinkSent": "Do vaší e-mailové schránky byl odeslán potvrzovací e-mail. Pro potvrzení požadavku na změnu hesla navštivte odkaz v e-mailu uvedený.", - "ContactAdmin": "Možná příčina: Váš hosting zakázal funkci mail().. <br \/>Prosím kontaktujte vašeho administrátora Piwiku.", + "ConfirmationLinkSent": "Do vaší schránky byl odeslán potvrzovací email. Pro potvrzení požadavku na změnu hesla navštivte odkaz uvedený v emailu.", + "ContactAdmin": "Možná příčina: Váš hosting zakázal funkci mail().. <br \/>Prosím kontaktujte svého administrátora Piwiku.", "ExceptionInvalidSuperUserAccessAuthenticationMethod": "Uživatel se Super uživatelským přístupem nemůže být autentizován mechanismem %s.", "ExceptionPasswordMD5HashExpected": "Parametr hesla je očekáván jako MD5 hash hesla", "InvalidNonceOrHeadersOrReferrer": "Zabezpečení formuláře selhalo. Prosím obnovte formulář a zkontrolujte, že máte povolené cookies. Pokud používáte proxy server, %smusíte nakonfigurovat Piwik aby přijímal proxy hlavičku%s, která přeposílá hlavičku hosta. Zkontrolujte také, že je správně posílána hlavička referrer.", "InvalidOrExpiredToken": "Klíč je neplatný, nebo vypršel", - "InvalidUsernameEmail": "Neplatné uživatelské jméno a\/nebo e-mailová adresa", + "InvalidUsernameEmail": "Neplatné uživatelské jméno a\/nebo emailová adresa", "LogIn": "Přihlásit", - "LoginOrEmail": "Uživatelské jméno nebo E-mail", - "LoginPasswordNotCorrect": "Uživatelské jméno & heslo nejsou správné", + "LoginOrEmail": "Uživatelské jméno nebo Email", + "LoginPasswordNotCorrect": "Chybná kombinace uživatelského jména a hesla.", "LostYourPassword": "Zapomněli jste vaše heslo?", "MailPasswordChangeBody": "Ahoj %1$s,\n\nZ %2$s byl zaslán požadavek o změnu hesla. Pro potvrzení nových přihlašovacích údajů navštivte následující odkaz:\n\n%3$s\n\nPoznámka: Tento token je platný 24 hodin.\n\nA díky za používání Piwiku!", "MailTopicPasswordChange": "Potvrďte změnu hesla", diff --git a/plugins/Login/lang/ko.json b/plugins/Login/lang/ko.json index bdc9d72937..e924445eda 100644 --- a/plugins/Login/lang/ko.json +++ b/plugins/Login/lang/ko.json @@ -2,6 +2,7 @@ "Login": { "ConfirmationLinkSent": "확인 링크가 당신의 받은 편지함으로 전송되었습니다. 이메일을 확인하고 비밀번호 변경요청을 승인하려면 이 링크를 방문하세요.", "ContactAdmin": "가능한 이유: 호스트가 메일 기능을 비활성화했을 수 있습니다. <br \/>당신의 Piwik 관리자에게 연락해보세요.", + "ExceptionInvalidSuperUserAccessAuthenticationMethod": "슈퍼 유저 접근의 '%s' 방법으로는 인증되지 않습니다.", "ExceptionPasswordMD5HashExpected": "비밀번호 매개변수는 MD5해시 값이 사용되고 있습니다.", "InvalidNonceOrHeadersOrReferrer": "양식 보안 실패, 양식을 새로고침하여 쿠키가 활성화되어 있는지 확인하세요. 프록시 서버를 사용하는 경우라면, 호스트 헤더에 %sPiwik configure에서 프록시 헤더를 수락%s하고 전달해야합니다. 또한 리퍼러 헤더가 올바르게 전송되는지 확인합니다.", "InvalidOrExpiredToken": "토큰이 잘못되었거나 만료되었습니다.", @@ -15,7 +16,8 @@ "PasswordChanged": "비밀번호가 변경되었습니다.", "PasswordRepeat": "비빌번호 (재입력)", "PasswordsDoNotMatch": "비밀번호가 일치하지 않습니다.", - "RememberMe": "저를 기억해 주세요", + "PluginDescription": "아이디와 비밀번호를 통해 사용자 인증뿐만 아니라 비밀번호 재설정 기능까지 제공합니다. 마켓에서 구할 수 있는 LoginLdap와 같은 또 다른 로그인 플러그인을 사용하여 인증 방법을 변경할 수 있습니다.", + "RememberMe": "계정 기억하기", "ResetPasswordInstructions": "계정에 사용할 새로운 비밀번호를 입력하세요." } }
\ No newline at end of file diff --git a/plugins/Login/lang/sl.json b/plugins/Login/lang/sl.json index e14d69523b..7c83e0c2b4 100644 --- a/plugins/Login/lang/sl.json +++ b/plugins/Login/lang/sl.json @@ -1,11 +1,17 @@ { "Login": { + "ConfirmationLinkSent": "Potrditveno sporočilo je bilo poslano na vaš elektronski naslov. Poglejte elektronsko pošto in sledite povezavi v njem, da potrdite zahtevo za spremembo vašega gesla.", + "ContactAdmin": "Možen razlog: na strežniku je onemogočena mail() funkcija. <br \/>Prosimo, obrnite se na vašega Piwik administratorja.", + "ExceptionInvalidSuperUserAccessAuthenticationMethod": "Uporabnik z vlogo \"Super User\" se ne more prijaviti z uporabo mehanizma '%s'.", "InvalidOrExpiredToken": "Žeton je neveljaven ali pa je potekel.", "InvalidUsernameEmail": "Napačno uporabniško ime ali e-mail naslov.", "LogIn": "Vpis", "LoginOrEmail": "Uporabniško ime ali E-mail", "LoginPasswordNotCorrect": "Napačna kombinacija uporabniškega imena in gesla.", "LostYourPassword": "Ste pozabili geslo?", + "MailPasswordChangeBody": "Pozdravljen\/-a %1$s,\n\ns strani %2$s je bil prejet zahtevek za zamenjavo gesla. Za potrditev verodostojnosti te zahteve, prosimo sledite povezavi:\n\n%3$s\n\nOpozorilo: ta žeton bo potekel po 24-ih urah.\n\nHvala, ker uporabljate Piwik!", + "MailTopicPasswordChange": "Potrdite spremembo gesla", + "PasswordChanged": "Vaše geslo je bilo spremenjeno.", "PasswordRepeat": "Geslo (ponovno)", "PasswordsDoNotMatch": "Gesli se ne ujemata.", "RememberMe": "Zapomni si me" diff --git a/plugins/Login/lang/tr.json b/plugins/Login/lang/tr.json index d9c1c638ec..981d278bf0 100644 --- a/plugins/Login/lang/tr.json +++ b/plugins/Login/lang/tr.json @@ -1,13 +1,18 @@ { "Login": { + "ConfirmationLinkSent": "Onaylama bağlantısı gelen kutunuza gönderildi. Şifre değişikliğinı onaylamak için e-posta hesabınızı kontrol edin ve bağlantıyı ziyaret edin.", + "ContactAdmin": "Olası nedeni: Sunucunuzda mail() fonksiyonunu devre dışı bırakmış olabilir. <br \/>Lütfen Piwik yöneticinizle iletişime geçiniz.", "InvalidOrExpiredToken": "Güvenlik kodu yanlış yada süresi dolmuş.", "InvalidUsernameEmail": "Geçersiz kullanıcı adı ve\/veya e-posta adresi", "LogIn": "Oturum aç", "LoginOrEmail": "Giriş veya E-posta", "LoginPasswordNotCorrect": "Kullanıcı adı & Şifre yanlış", "LostYourPassword": "Şifrenizi mi unuttunuz?", + "MailTopicPasswordChange": "Parola Değişikliğini Onayla", + "PasswordChanged": "Parolanız değiştirildi.", "PasswordRepeat": "Şifre (Tekrar)", "PasswordsDoNotMatch": "Şifre eşleşmedi", - "RememberMe": "Beni Hatırla" + "RememberMe": "Beni Hatırla", + "ResetPasswordInstructions": "Hesabınız için yeni parolanızı giriniz." } }
\ No newline at end of file diff --git a/plugins/Login/templates/login.twig b/plugins/Login/templates/login.twig index 4bcef0d799..0140418af4 100644 --- a/plugins/Login/templates/login.twig +++ b/plugins/Login/templates/login.twig @@ -1,12 +1,12 @@ {% extends '@Morpheus/layout.twig' %} +{% block meta %} + <meta name="robots" content="index,follow"> +{% endblock %} + {% block head %} {{ parent() }} - {% block meta %} - <meta name="robots" content="index,follow"> - {% endblock %} - <script type="text/javascript" src="libs/bower_components/jquery-placeholder/jquery.placeholder.js"></script> <!--[if lt IE 9]> <script src="libs/bower_components/html5shiv/dist/html5shiv.min.js"></script> diff --git a/plugins/MobileAppMeasurable/lang/ja.json b/plugins/MobileAppMeasurable/lang/ja.json new file mode 100644 index 0000000000..deee803801 --- /dev/null +++ b/plugins/MobileAppMeasurable/lang/ja.json @@ -0,0 +1,7 @@ +{ + "MobileAppMeasurable": { + "MobileApp": "モバイルアプリ", + "MobileApps": "モバイルアプリ", + "MobileAppDescription": "IOS、Android や他のモバイル オペレーティング システムのネイティブ モバイル アプリケーション。" + } +}
\ No newline at end of file diff --git a/plugins/MobileAppMeasurable/lang/nb.json b/plugins/MobileAppMeasurable/lang/nb.json new file mode 100644 index 0000000000..49eee5557f --- /dev/null +++ b/plugins/MobileAppMeasurable/lang/nb.json @@ -0,0 +1,7 @@ +{ + "MobileAppMeasurable": { + "MobileApp": "Mobil-app", + "MobileApps": "Mobil-apper", + "MobileAppDescription": "En spesialbygget mobil-app for iOS, Android eller hvilken som helst annen mobil plattform." + } +}
\ No newline at end of file diff --git a/plugins/MobileMessaging/API.php b/plugins/MobileMessaging/API.php index aaf30e6a64..345c633b15 100644 --- a/plugins/MobileMessaging/API.php +++ b/plugins/MobileMessaging/API.php @@ -8,7 +8,6 @@ */ namespace Piwik\Plugins\MobileMessaging; -use Piwik\Common; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugins\MobileMessaging\SMSProvider; @@ -27,15 +26,6 @@ class API extends \Piwik\Plugin\API const SMS_FROM = 'Piwik'; /** - * @param string $provider - * @return SMSProvider - */ - private static function getSMSProviderInstance($provider) - { - return SMSProvider::factory($provider); - } - - /** * determine if SMS API credential are available for the current user * * @return bool true if SMS API credential are available for the current user @@ -83,7 +73,7 @@ class API extends \Piwik\Plugin\API { $this->checkCredentialManagementRights(); - $smsProviderInstance = self::getSMSProviderInstance($provider); + $smsProviderInstance = SMSProvider::factory($provider); $smsProviderInstance->verifyCredential($apiKey); $settings = $this->getCredentialManagerSettings(); @@ -160,7 +150,7 @@ class API extends \Piwik\Plugin\API Piwik::checkUserIsNotAnonymous(); $credential = $this->getSMSAPICredential(); - $SMSProvider = self::getSMSProviderInstance($credential[MobileMessaging::PROVIDER_OPTION]); + $SMSProvider = SMSProvider::factory($credential[MobileMessaging::PROVIDER_OPTION]); $SMSProvider->sendSMS( $credential[MobileMessaging::API_KEY_OPTION], $content, @@ -183,7 +173,7 @@ class API extends \Piwik\Plugin\API $this->checkCredentialManagementRights(); $credential = $this->getSMSAPICredential(); - $SMSProvider = self::getSMSProviderInstance($credential[MobileMessaging::PROVIDER_OPTION]); + $SMSProvider = SMSProvider::factory($credential[MobileMessaging::PROVIDER_OPTION]); return $SMSProvider->getCreditLeft( $credential[MobileMessaging::API_KEY_OPTION] ); diff --git a/plugins/MobileMessaging/Controller.php b/plugins/MobileMessaging/Controller.php index 2bb6f8d5f5..7ba976c67e 100644 --- a/plugins/MobileMessaging/Controller.php +++ b/plugins/MobileMessaging/Controller.php @@ -94,7 +94,12 @@ class Controller extends ControllerAdmin $view->creditLeft = $mobileMessagingAPI->getCreditLeft(); } - $view->smsProviders = SMSProvider::getAvailableSMSProviders(); + $providers = array(); + foreach (SMSProvider::findAvailableSmsProviders() as $provider) { + $providers[$provider->getId()] = $provider->getDescription(); + } + + $view->smsProviders = $providers; // construct the list of countries from the lang files $countries = array(); diff --git a/plugins/MobileMessaging/SMSProvider.php b/plugins/MobileMessaging/SMSProvider.php index 1711bfafed..14aec1bb24 100644 --- a/plugins/MobileMessaging/SMSProvider.php +++ b/plugins/MobileMessaging/SMSProvider.php @@ -8,63 +8,120 @@ */ namespace Piwik\Plugins\MobileMessaging; -use Exception; -use Piwik\Development; +use Piwik\Container\StaticContainer; +use Piwik\Plugin; use Piwik\Piwik; -use Piwik\BaseFactory; /** - * The SMSProvider abstract class is used as a base class for SMS provider implementations. + * The SMSProvider abstract class is used as a base class for SMS provider implementations. To create your own custom + * SMSProvider extend this class and implement the methods to send text messages. The class needs to be placed in a + * `SMSProvider` directory of your plugin. * + * @api */ -abstract class SMSProvider extends BaseFactory +abstract class SMSProvider { const MAX_GSM_CHARS_IN_ONE_UNIQUE_SMS = 160; const MAX_GSM_CHARS_IN_ONE_CONCATENATED_SMS = 153; const MAX_UCS2_CHARS_IN_ONE_UNIQUE_SMS = 70; const MAX_UCS2_CHARS_IN_ONE_CONCATENATED_SMS = 67; - protected static $availableSMSProviders = array( - 'Clockwork' => 'You can use <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/"><img src="plugins/MobileMessaging/images/Clockwork.png"/></a> to send SMS Reports from Piwik.<br/> - <ul> - <li> First, <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/">get an API Key from Clockwork</a> (Signup is free!) - </li><li> Enter your Clockwork API Key on this page. </li> - </ul> - <br/><em>About Clockwork: </em><ul> - <li>Clockwork gives you fast, reliable high quality worldwide SMS delivery, over 450 networks in every corner of the globe. - </li><li>Cost per SMS message is around ~0.08USD (0.06EUR). - </li><li>Most countries and networks are supported but we suggest you check the latest position on their coverage map <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/sms-coverage/">here</a>. - </li> - </ul> - ', - ); - - protected static function getClassNameFromClassId($id) + /** + * Get the ID of the SMS Provider. Eg 'Clockwork' or 'FreeMobile' + * @return string + */ + abstract public function getId(); + + /** + * Get a description about the SMS Provider. For example who the SMS Provider is, instructions how the API Key + * needs to be set, and more. You may return HTML here for better formatting. + * + * @return string + */ + abstract public function getDescription(); + + /** + * Verify the SMS API credential. + * + * @param string $apiKey API Key + * @return bool true if SMS API Key is valid, false otherwise + */ + abstract public function verifyCredential($apiKey); + + /** + * Get the amount of remaining credits. + * + * @param string $apiKey API Key + * @return string remaining credits + */ + abstract public function getCreditLeft($apiKey); + + /** + * Actually send the given text message. This method should only send the text message, it should not trigger + * any notifications etc. + * + * @param string $apiKey + * @param string $smsText + * @param string $phoneNumber + * @param string $from + * @return bool true + */ + abstract public function sendSMS($apiKey, $smsText, $phoneNumber, $from); + + /** + * Defines whether the SMS Provider is available. If a certain provider should be used only be a limited + * range of users you can restrict the provider here. For example there is a Development SMS Provider that is only + * available when the development is actually enabled. You could also create a SMS Provider that is only available + * to Super Users etc. Usually this method does not have to be implemented by a SMS Provider. + * + * @return bool + */ + public function isAvailable() { - return __NAMESPACE__ . '\\SMSProvider\\' . $id; + return true; } - protected static function getInvalidClassIdExceptionMessage($id) + /** + * @param string $provider The name of the string + * @return SMSProvider + * @throws \Exception + * @ignore + */ + public static function factory($provider) { - return Piwik::translate('MobileMessaging_Exception_UnknownProvider', - array($id, implode(', ', array_keys(self::getAvailableSMSProviders()))) - ); + $providers = self::findAvailableSmsProviders(); + + if (!array_key_exists($provider, $providers)) { + throw new \Exception(Piwik::translate('MobileMessaging_Exception_UnknownProvider', + array($provider, implode(', ', array_keys($providers))) + )); + } + + return $providers[$provider]; } /** * Returns all available SMS Providers - * - * @return array + * + * @return SMSProvider[] + * @ignore */ - public static function getAvailableSMSProviders() + public static function findAvailableSmsProviders() { - $smsProviders = self::$availableSMSProviders; + /** @var SMSProvider[] $smsProviders */ + $smsProviders = Plugin\Manager::getInstance()->findMultipleComponents('SMSProvider', 'Piwik\Plugins\MobileMessaging\SMSProvider'); + + $providers = array(); - if (Development::isEnabled()) { - $smsProviders['Development'] = 'Development SMS Provider<br />All sent SMS will be displayed as Notification'; + foreach ($smsProviders as $provider) { + /** @var SMSProvider $provider */ + $provider = StaticContainer::get($provider); + if ($provider->isAvailable()) { + $providers[$provider->getId()] = $provider; + } } - return $smsProviders; + return $providers; } /** @@ -72,6 +129,7 @@ abstract class SMSProvider extends BaseFactory * * @param string $string * @return bool true if $string contains UCS2 characters + * @ignore */ public static function containsUCS2Characters($string) { @@ -94,6 +152,7 @@ abstract class SMSProvider extends BaseFactory * @param int $maximumNumberOfConcatenatedSMS * @param string $appendedString * @return string original $string or truncated $string appended with $appendedString + * @ignore */ public static function truncate($string, $maximumNumberOfConcatenatedSMS, $appendedString = 'MobileMessaging_SMS_Content_Too_Long') { @@ -150,30 +209,4 @@ abstract class SMSProvider extends BaseFactory $maxCharsInOneConcatenatedSMS * $maximumNumberOfConcatenatedSMS; } - /** - * verify the SMS API credential - * - * @param string $apiKey API Key - * @return bool true if SMS API credential are valid, false otherwise - */ - abstract public function verifyCredential($apiKey); - - /** - * get remaining credits - * - * @param string $apiKey API Key - * @return string remaining credits - */ - abstract public function getCreditLeft($apiKey); - - /** - * send SMS - * - * @param string $apiKey - * @param string $smsText - * @param string $phoneNumber - * @param string $from - * @return bool true - */ - abstract public function sendSMS($apiKey, $smsText, $phoneNumber, $from); } diff --git a/plugins/MobileMessaging/SMSProvider/Clockwork.php b/plugins/MobileMessaging/SMSProvider/Clockwork.php index 2ae8e95da4..50a7f0e50b 100644 --- a/plugins/MobileMessaging/SMSProvider/Clockwork.php +++ b/plugins/MobileMessaging/SMSProvider/Clockwork.php @@ -15,8 +15,9 @@ use Piwik\Plugins\MobileMessaging\APIException; use Piwik\Plugins\MobileMessaging\SMSProvider; require_once PIWIK_INCLUDE_PATH . "/plugins/MobileMessaging/APIException.php"; + /** - * + * @ignore */ class Clockwork extends SMSProvider { @@ -31,6 +32,27 @@ class Clockwork extends SMSProvider const MAXIMUM_FROM_LENGTH = 11; const MAXIMUM_CONCATENATED_SMS = 3; + public function getId() + { + return 'Clockwork'; + } + + public function getDescription() + { + return 'You can use <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/"><img src="plugins/MobileMessaging/images/Clockwork.png"/></a> to send SMS Reports from Piwik.<br/> + <ul> + <li> First, <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/platforms/piwik/">get an API Key from Clockwork</a> (Signup is free!) + </li><li> Enter your Clockwork API Key on this page. </li> + </ul> + <br/><em>About Clockwork: </em><ul> + <li>Clockwork gives you fast, reliable high quality worldwide SMS delivery, over 450 networks in every corner of the globe. + </li><li>Cost per SMS message is around ~0.08USD (0.06EUR). + </li><li>Most countries and networks are supported but we suggest you check the latest position on their coverage map <a target="_blank" href="?module=Proxy&action=redirect&url=http://www.clockworksms.com/sms-coverage/">here</a>. + </li> + </ul> + '; + } + public function verifyCredential($apiKey) { $this->getCreditLeft($apiKey); diff --git a/plugins/MobileMessaging/SMSProvider/Development.php b/plugins/MobileMessaging/SMSProvider/Development.php index ef6cbe951d..18cc57809a 100644 --- a/plugins/MobileMessaging/SMSProvider/Development.php +++ b/plugins/MobileMessaging/SMSProvider/Development.php @@ -10,14 +10,31 @@ namespace Piwik\Plugins\MobileMessaging\SMSProvider; use Piwik\Notification; use Piwik\Plugins\MobileMessaging\SMSProvider; +use Piwik\Development as PiwikDevelopment; /** * Used for development only * + * @ignore */ class Development extends SMSProvider { + public function getId() + { + return 'Development'; + } + + public function getDescription() + { + return 'Development SMS Provider<br />All sent SMS will be displayed as Notification'; + } + + public function isAvailable() + { + return PiwikDevelopment::isEnabled(); + } + public function verifyCredential($apiKey) { return true; diff --git a/plugins/MobileMessaging/SMSProvider/StubbedProvider.php b/plugins/MobileMessaging/SMSProvider/StubbedProvider.php index b9527a3b8d..8bda726a3e 100644 --- a/plugins/MobileMessaging/SMSProvider/StubbedProvider.php +++ b/plugins/MobileMessaging/SMSProvider/StubbedProvider.php @@ -13,10 +13,26 @@ use Piwik\Plugins\MobileMessaging\SMSProvider; /** * Used for testing * + * @ignore */ class StubbedProvider extends SMSProvider { + public function getId() + { + return 'StubbedProvider'; + } + + public function getDescription() + { + return 'Only during testing available'; + } + + public function isAvailable() + { + return defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE; + } + public function verifyCredential($apiKey) { return true; diff --git a/plugins/MobileMessaging/lang/cs.json b/plugins/MobileMessaging/lang/cs.json index efe1d113af..6b4d0ad68b 100644 --- a/plugins/MobileMessaging/lang/cs.json +++ b/plugins/MobileMessaging/lang/cs.json @@ -6,24 +6,24 @@ "MobileReport_NoPhoneNumbers": "Prosím aktivujte aspoň jedno telefonní číslo přejitím na", "MultiSites_Must_Be_Activated": "Pro vytváření SMS zpráv se statistikami povolte v Piwiku plugin MultiSites.", "PhoneNumbers": "Telefonní čísla", - "PluginDescription": "Vytvářejte a stahujte vlastní SMS hlášení a nechte si je zasílat do vašeho telefonu denně, týdnnebo ě měsíčně.", + "PluginDescription": "Vytvářejte a stahujte vlastní SMS hlášení a nechte si je zasílat do svého telefonu denně, týdně nebo měsíčně.", "Settings_APIKey": "Klíč k API", "Settings_CountryCode": "Kód země", - "Settings_CredentialNotProvided": "Než budete moct vytvářet a spravovat telefonní čísla, musíte připojit Piwik k vašemu SMS účtu výše.", - "Settings_CredentialNotProvidedByAdmin": "Než budete moct vytvářet a spravovat telefonní čísla, požádejte vašeho administrátora, aby připojil Piwik k SMS účtu.", + "Settings_CredentialNotProvided": "Než budete moci vytvářet a spravovat telefonní čísla, musíte připojit Piwik ke svému SMS účtu výše.", + "Settings_CredentialNotProvidedByAdmin": "Než budete moci vytvářet a spravovat telefonní čísla, požádejte svého administrátora, aby připojil Piwik k SMS účtu.", "Settings_CredentialProvided": ";Váš %s SMS účet je nastaven správně.", "Settings_DeleteAccountConfirm": "Opravdu chcete odstranit tento SMS účet?", "Settings_InvalidActivationCode": "Zadaný kód nebyl platný, prosím zkuste to znovu.", "Settings_LetUsersManageAPICredential": "Povolit uživatelům správu vlastních pověření API SMS účtů", "Settings_LetUsersManageAPICredential_No_Help": "Všichni uživatelé mohou přijímat hlášení prostřednictvím SMS a budou používat kredity vašeho účtu.", - "Settings_LetUsersManageAPICredential_Yes_Help": "Každý uživatel bude moct nastavit svůj SMS API účet an nebude používat váš kredit.", + "Settings_LetUsersManageAPICredential_Yes_Help": "Každý uživatel bude moci nastavit svůj SMS API účet a nebude používat váš kredit.", "Settings_ManagePhoneNumbers": "Spravovat telefonní čísla", "Settings_PhoneActivated": "Telefonní číslo ověřeno! Nyní můžete přijímat SMS se statistikami.", "Settings_PhoneNumber": "Telefonní číslo", "Settings_PhoneNumbers_Add": "Přidat nové telefonní číslo", "Settings_PhoneNumbers_CountryCode_Help": "Pokud nevíte telefonní kód vaší země, podívejte se zde", - "Settings_PhoneNumbers_Help": "Než budete moct přijímat SMS se statistikami, telefonní číslo musí být zadáno níže.", - "Settings_PhoneNumbers_HelpAdd": "Po stisknutí \"přidat\" bude na telefon odeslána SMS s kódem. Uživatel, který obdrží SMS by se měl přihlásit do Piwiku, kliknout na nastavení, pak na Mobilní zprávy a zadat kód. Poté bude moct dostávat statistiky do svého telefonu.", + "Settings_PhoneNumbers_Help": "Než budete moci přijímat SMS se statistikami, telefonní číslo musí být zadáno níže.", + "Settings_PhoneNumbers_HelpAdd": "Po stisknutí \"přidat\" bude na telefon odeslána SMS s kódem. Uživatel, který obdrží SMS by se měl přihlásit do Piwiku, kliknout na nastavení, pak na Mobilní zprávy a zadat kód. Poté bude moci dostávat statistiky do svého telefonu.", "Settings_PleaseSignUp": "Pokud chcetevytvářet SMS hlášení a dostávat krátké zprávy o statistikách do svého telefonu, zaregistrujte se na SMS API a zadejte svoje informace níže.", "Settings_SMSAPIAccount": "Správa SMS API účtu", "Settings_SMSProvider": "SMS Provider", @@ -34,8 +34,8 @@ "Settings_VerificationCodeJustSent": "Právě jsme na zadané telefonní číslo odeslali SMS s kódem. Zadejte ho výše a stiskněte \"ověřit\".", "SettingsMenu": "Mobilní zprávy", "SMS_Content_Too_Long": "[příliš dlouhé]", - "TopLinkTooltip": "Nechte si webové analýzi zasílat na e-mail nebo na váš mobilní telefon.", - "TopMenu": "Email & SMS Reporty", + "TopLinkTooltip": "Nechte si webové analýzy zasílat na email nebo na svůj mobilní telefon.", + "TopMenu": "Email a SMS reporty", "VerificationText": "Kód je %s. Pro ověření vašeh otelefonního čísla tento kód zkopírujte do formuláře přístupného na Piwik > %s > %s." } }
\ No newline at end of file diff --git a/plugins/MobileMessaging/lang/tr.json b/plugins/MobileMessaging/lang/tr.json new file mode 100644 index 0000000000..3a034272ba --- /dev/null +++ b/plugins/MobileMessaging/lang/tr.json @@ -0,0 +1,22 @@ +{ + "MobileMessaging": { + "PhoneNumbers": "Telefon Numaraları", + "Settings_APIKey": "API Anahtarı", + "Settings_CountryCode": "Ülke Kodu", + "Settings_DeleteAccountConfirm": "Bu SMS hesabını silmek istediğinizden emin misiniz?", + "Settings_InvalidActivationCode": "Girilen kod geçerli değil, lütfen tekrar deneyin.", + "Settings_ManagePhoneNumbers": "Telefon Numaralarını Yönet", + "Settings_PhoneActivated": "Telefon numarası doğrulandı! Artık istatistikleri SMS ile alabilirsiniz.", + "Settings_PhoneNumber": "Telefon Numarası", + "Settings_PhoneNumbers_Add": "Yeni telefon numarası ekle", + "Settings_PhoneNumbers_CountryCode_Help": "Telefon ülke kodunu bilmiyorsanız, buradan ülkelere bakabilirsiniz.", + "Settings_SMSAPIAccount": "SMS API Hesabını Yönet", + "Settings_SMSProvider": "SMS Sağlayıcı", + "Settings_ValidatePhoneNumber": "Onayla", + "Settings_VerificationCodeJustSent": "Biz az önce SMS ile numaranıza kod gönderdik: lütfen yukarıya kodu girin ve \"Onayla\" 'ya tıklayın.", + "SettingsMenu": "Mobil Mesajlaşma", + "SMS_Content_Too_Long": "[çok uzun]", + "TopLinkTooltip": "Web Analytics Raporlarını e-posta kutunuza veya cep telefonunuza teslim alın.", + "TopMenu": "E-posta ve SMS Raporları" + } +}
\ No newline at end of file diff --git a/plugins/Monolog/tests/System/TrackerLoggingTest.php b/plugins/Monolog/tests/System/TrackerLoggingTest.php index 70c933d13f..8716229116 100644 --- a/plugins/Monolog/tests/System/TrackerLoggingTest.php +++ b/plugins/Monolog/tests/System/TrackerLoggingTest.php @@ -83,10 +83,8 @@ DEBUG: 'apiv' => '1',", $response); private function setTrackerConfig($trackerConfig) { $testingEnvironment = self::$fixture->getTestEnvironment(); - $configOverride = $testingEnvironment->configOverride; - $configOverride['Tracker'] = $trackerConfig; - $configOverride['log']['log_writers'] = array('screen'); - $testingEnvironment->configOverride = $configOverride; + $testingEnvironment->overrideConfig('Tracker', $trackerConfig); + $testingEnvironment->overrideConfig('log', 'log_writers', array('screen')); $testingEnvironment->save(); } diff --git a/plugins/MultiSites/API.php b/plugins/MultiSites/API.php index 60b9fab90a..23ae821738 100755 --- a/plugins/MultiSites/API.php +++ b/plugins/MultiSites/API.php @@ -147,7 +147,6 @@ class API extends \Piwik\Plugin\API 'limit' => SettingsPiwik::getWebsitesCountToDisplay(), 'showColumns' => '', 'hideColumns' => '', - 'serialize' => 0, 'format' => 'original')); if (!empty($sites)) { diff --git a/plugins/MultiSites/lang/ko.json b/plugins/MultiSites/lang/ko.json index c7bdb7ee7b..69e154b027 100644 --- a/plugins/MultiSites/lang/ko.json +++ b/plugins/MultiSites/lang/ko.json @@ -1,6 +1,7 @@ { "MultiSites": { "Evolution": "변화 추이", - "TopLinkTooltip": "웹사이트의 모든 웹분석 통계를 비교합니다." + "TopLinkTooltip": "웹사이트의 모든 웹분석 통계를 비교합니다.", + "Pagination": "%s - %s 의 %s" } }
\ No newline at end of file diff --git a/plugins/Overlay/Controller.php b/plugins/Overlay/Controller.php index 795d7b991d..6327d34ddf 100644 --- a/plugins/Overlay/Controller.php +++ b/plugins/Overlay/Controller.php @@ -16,14 +16,26 @@ use Piwik\Metrics; use Piwik\Piwik; use Piwik\Plugin\Report; use Piwik\Plugins\Actions\ArchivingHelper; +use Piwik\Plugins\SegmentEditor\SegmentFormatter; use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\ProxyHttp; +use Piwik\Segment; use Piwik\Tracker\Action; use Piwik\Tracker\PageUrl; use Piwik\View; class Controller extends \Piwik\Plugin\Controller { + /** + * @var SegmentFormatter + */ + private $segmentFormatter; + + public function __construct(SegmentFormatter $segmentFormatter) + { + $this->segmentFormatter = $segmentFormatter; + parent::__construct(); + } /** The index of the plugin */ public function index() @@ -38,6 +50,7 @@ class Controller extends \Piwik\Plugin\Controller $view = new View($template); $this->setGeneralVariablesView($view); + $view->segment = Request::getRawSegmentFromRequest(); $view->ssl = ProxyHttp::isHttps(); @@ -52,8 +65,9 @@ class Controller extends \Piwik\Plugin\Controller $period = Common::getRequestVar('period'); $date = Common::getRequestVar('date'); $currentUrl = Common::getRequestVar('currentUrl'); + $segment = Request::getRawSegmentFromRequest(); $currentUrl = Common::unsanitizeInputValue($currentUrl); - $segment = ''; + $segmentSidebar = ''; $normalizedCurrentUrl = PageUrl::excludeQueryParametersFromUrl($currentUrl, $idSite); $normalizedCurrentUrl = Common::unsanitizeInputValue($normalizedCurrentUrl); @@ -63,16 +77,21 @@ class Controller extends \Piwik\Plugin\Controller $path = ArchivingHelper::getActionExplodedNames($normalizedCurrentUrl, Action::TYPE_PAGE_URL); $path = array_map('urlencode', $path); $label = implode('>', $path); - $request = new Request( - 'method=Actions.getPageUrls' - . '&idSite=' . urlencode($idSite) - . '&date=' . urlencode($date) - . '&period=' . urlencode($period) - . '&label=' . urlencode($label) - . '&format=original' - . '&format_metrics=0' + + $params = array( + 'idSite' => $idSite, + 'date' => $date, + 'period' => $period, + 'label' => $label, + 'format' => 'original', + 'format_metrics' => 0, ); - $dataTable = $request->process(); + + if (!empty($segment)) { + $params['segment'] = $segment; + } + + $dataTable = Request::processRequest('Actions.getPageUrls', $params); $formatter = new Metrics\Formatter\Html(); @@ -84,7 +103,10 @@ class Controller extends \Piwik\Plugin\Controller $showMetrics = array('nb_hits', 'nb_visits', 'nb_users', 'nb_uniq_visitors', 'bounce_rate', 'exit_rate', 'avg_time_on_page'); - $segment = $row->getMetadata('segment'); + $segmentSidebar = $row->getMetadata('segment'); + if (!empty($segmentSidebar) && !empty($segment)) { + $segmentSidebar = $segment . ';' . $segmentSidebar; + } foreach ($showMetrics as $metric) { $value = $row->getColumn($metric); @@ -127,7 +149,8 @@ class Controller extends \Piwik\Plugin\Controller $view->idSite = $idSite; $view->period = $period; $view->date = $date; - $view->segment = $segment; + $view->segment = $segmentSidebar; + $view->segmentDescription = $this->segmentFormatter->getHumanReadable($segment, $idSite); $this->outputCORSHeaders(); return $view->render(); @@ -142,64 +165,20 @@ class Controller extends \Piwik\Plugin\Controller $idSite = Common::getRequestVar('idSite', 0, 'int'); Piwik::checkUserHasViewAccess($idSite); + $view = new View('@Overlay/startOverlaySession'); + $sitesManager = APISitesManager::getInstance(); $site = $sitesManager->getSiteFromId($idSite); $urls = $sitesManager->getSiteUrlsFromId($idSite); + $view->isHttps = ProxyHttp::isHttps(); + $view->knownUrls = json_encode($urls); + $view->mainUrl = $site['main_url']; + $this->outputCORSHeaders(); Common::sendHeader('Content-Type: text/html; charset=UTF-8'); - return ' - <html><head><title></title></head><body> - <script type="text/javascript"> - function handleProtocol(url) { - if (' . (ProxyHttp::isHttps() ? 'true' : 'false') . ') { - return url.replace(/http:\/\//i, "https://"); - } else { - return url.replace(/https:\/\//i, "http://"); - } - } - - function removeUrlPrefix(url) { - return url.replace(/http(s)?:\/\/(www\.)?/i, ""); - } - - if (window.location.hash) { - var match = false; - - var urlToRedirect = window.location.hash.substr(1); - var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect); - - var knownUrls = ' . json_encode($urls) . '; - for (var i = 0; i < knownUrls.length; i++) { - var testUrl = removeUrlPrefix(knownUrls[i]); - if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) { - match = true; - if (navigator.appName == "Microsoft Internet Explorer") { - // internet explorer loses the referrer if we use window.location.href=X - var referLink = document.createElement("a"); - referLink.href = handleProtocol(urlToRedirect); - document.body.appendChild(referLink); - referLink.click(); - } else { - window.location.href = handleProtocol(urlToRedirect); - } - break; - } - } - - if (!match) { - var idSite = window.location.href.match(/idSite=([0-9]+)/i)[1]; - window.location.href = "index.php?module=Overlay&action=showErrorWrongDomain" - + "&idSite=" + idSite - + "&url=" + encodeURIComponent(urlToRedirect); - } - } - else { - window.location.href = handleProtocol("' . $site['main_url'] . '"); - }; - </script> - </body></html> - '; + + return $view->render(); } /** diff --git a/plugins/Overlay/client/client.js b/plugins/Overlay/client/client.js index 75bd19f3b3..5fd99c466b 100644 --- a/plugins/Overlay/client/client.js +++ b/plugins/Overlay/client/client.js @@ -10,7 +10,7 @@ var Piwik_Overlay_Client = (function () { var idSite; /** The current period and date */ - var period, date; + var period, date, segment; /** Reference to the status bar DOM element */ var statusBar; @@ -84,11 +84,12 @@ var Piwik_Overlay_Client = (function () { return { /** Initialize in-site analytics */ - initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate) { + initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate, pSegment) { piwikRoot = pPiwikRoot; idSite = pIdSite; period = pPeriod; date = pDate; + segment = pSegment; var load = this.loadScript; var loading = this.loadingNotification; @@ -146,6 +147,10 @@ var Piwik_Overlay_Client = (function () { var url = piwikRoot + 'index.php?module=API&method=Overlay.' + method + '&idSite=' + idSite + '&period=' + period + '&date=' + date + '&format=JSON&filter_limit=-1'; + if (segment) { + url += '&segment=' + segment; + } + if (additionalParams) { url += '&' + additionalParams; } diff --git a/plugins/Overlay/config/ui-test.php b/plugins/Overlay/config/ui-test.php new file mode 100644 index 0000000000..1bb5ae8b75 --- /dev/null +++ b/plugins/Overlay/config/ui-test.php @@ -0,0 +1,14 @@ +<?php + +return array( + + // Overlay needs the full URLs in order to find the links in the embedded page (otherwise the % + // tooltips don't show up) + 'tests.ui.url_normalizer_blacklist.api' => DI\add(array( + 'Overlay.getFollowingPages', + )), + 'tests.ui.url_normalizer_blacklist.controller' => DI\add(array( + 'Overlay.renderSidebar', + )), + +);
\ No newline at end of file diff --git a/plugins/Overlay/javascripts/Overlay_Helper.js b/plugins/Overlay/javascripts/Overlay_Helper.js index e0681e7b45..42ff388d81 100644 --- a/plugins/Overlay/javascripts/Overlay_Helper.js +++ b/plugins/Overlay/javascripts/Overlay_Helper.js @@ -20,11 +20,17 @@ var Overlay_Helper = { }, /** Get the url to launch overlay */ - getOverlayLink: function (idSite, period, date, link) { + getOverlayLink: function (idSite, period, date, segment, link) { var url = 'index.php?module=Overlay&period=' + encodeURIComponent(period) + '&date=' + encodeURIComponent(date) + '&idSite=' + encodeURIComponent(idSite); + + if (segment) { + url += '&segment=' + encodeURIComponent(segment); + } + if (link) { url += '#?l=' + Overlay_Helper.encodeFrameUrl(link); } + return url; } diff --git a/plugins/Overlay/javascripts/Piwik_Overlay.js b/plugins/Overlay/javascripts/Piwik_Overlay.js index 4c78080806..4bde0ee343 100644 --- a/plugins/Overlay/javascripts/Piwik_Overlay.js +++ b/plugins/Overlay/javascripts/Piwik_Overlay.js @@ -10,7 +10,7 @@ var Piwik_Overlay = (function () { var $body, $iframe, $sidebar, $main, $location, $loading, $errorNotLoading; var $rowEvolutionLink, $transitionsLink, $fullScreenLink, $visitorLogLink; - var idSite, period, date; + var idSite, period, date, segment; var iframeSrcBase; var iframeDomain = ''; @@ -28,13 +28,19 @@ var Piwik_Overlay = (function () { iframeCurrentPage = currentUrl; iframeDomain = currentUrl.match(/http(s)?:\/\/(www\.)?([^\/]*)/i)[3]; - globalAjaxQueue.abort(); - var ajaxRequest = new ajaxHelper(); - ajaxRequest.addParams({ + var params = { module: 'Overlay', action: 'renderSidebar', currentUrl: currentUrl - }, 'get'); + }; + + if (segment) { + params.segment = segment; + } + + globalAjaxQueue.abort(); + var ajaxRequest = new ajaxHelper(); + ajaxRequest.addParams(params, 'get'); ajaxRequest.setCallback( function (response) { hideLoading(); @@ -111,6 +117,16 @@ var Piwik_Overlay = (function () { $fullScreenLink.show(); } + function getOverlaySegment(url) { + var location = broadcast.getParamValue('segment', url); + + // angular will encode the value again since it is added as the fragment path, not the fragment query parameter, + // so we have to decode it again after getParamValue + location = decodeURIComponent(location); + + return location; + } + function getOverlayLocationFromHash(urlHash) { var location = broadcast.getParamValue('l', urlHash); @@ -143,11 +159,12 @@ var Piwik_Overlay = (function () { return { /** This method is called when Overlay loads */ - init: function (iframeSrc, pIdSite, pPeriod, pDate) { + init: function (iframeSrc, pIdSite, pPeriod, pDate, pSegment) { iframeSrcBase = iframeSrc; idSite = pIdSite; period = pPeriod; date = pDate; + segment = pSegment; $body = $('body'); $iframe = $('#overlayIframe'); @@ -201,7 +218,7 @@ var Piwik_Overlay = (function () { if (parts.length == 2) { period = parts[0]; date = parts[1]; - window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, iframeCurrentPage); + window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, segment, iframeCurrentPage); } }); @@ -219,7 +236,11 @@ var Piwik_Overlay = (function () { // handle transitions link $transitionsLink.click(function () { - DataTable_RowActions_Transitions.launchForUrl(iframeCurrentPageNormalized); + var unescapedSegment = null; + if (segment) { + unescapedSegment = unescape(segment); + } + DataTable_RowActions_Transitions.launchForUrl(iframeCurrentPageNormalized, unescapedSegment); return false; }); diff --git a/plugins/Overlay/javascripts/rowaction.js b/plugins/Overlay/javascripts/rowaction.js index c84e60a33b..de063b990e 100644 --- a/plugins/Overlay/javascripts/rowaction.js +++ b/plugins/Overlay/javascripts/rowaction.js @@ -15,17 +15,47 @@ function DataTable_RowActions_Overlay(dataTable) { DataTable_RowActions_Overlay.prototype = new DataTable_RowAction; +DataTable_RowActions_Overlay.registeredReports = []; +DataTable_RowActions_Overlay.registerReport = function (handler) { + DataTable_RowActions_Overlay.registeredReports.push(handler); +} + + DataTable_RowActions_Overlay.prototype.onClick = function (actionA, tr, e) { if (!actionA.data('overlay-manipulated')) { actionA.data('overlay-manipulated', 1); - var link = tr.find('> td:first > a').attr('href'); - link = $('<textarea>').html(link).val(); // remove html entities + var segment, link; + + var i = 0; + for (i; i < DataTable_RowActions_Overlay.registeredReports.length; i++) { + var report = DataTable_RowActions_Overlay.registeredReports[i]; + if (report + && report.onClick + && report.isAvailableOnReport + && report.isAvailableOnReport(this.dataTable.param)) { + var result = report.onClick.apply(this, arguments); + + if (!result || !result.link) { + return; + } + + link = result.link; + if (result.segment) { + segment = result.segment; + } + break; + } + } + + if (link) { + var href = Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', segment, link); - actionA.attr({ - target: '_blank', - href: Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', link) - }); + actionA.attr({ + target: '_blank', + href: href + }); + } } return true; @@ -54,7 +84,18 @@ DataTable_RowActions_Registry.register({ if (!window.DataTable_RowActions_Transitions) { return false; } - return DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action); + + var i = 0; + for (i; i < DataTable_RowActions_Overlay.registeredReports.length; i++) { + var report = DataTable_RowActions_Overlay.registeredReports[i]; + if (report + && report.isAvailableOnReport + && report.isAvailableOnReport(dataTableParams)) { + return true; + } + } + + return false; }, isAvailableOnRow: function (dataTableParams, tr) { diff --git a/plugins/Overlay/lang/ja.json b/plugins/Overlay/lang/ja.json index 952ea205ac..3c99e23c55 100644 --- a/plugins/Overlay/lang/ja.json +++ b/plugins/Overlay/lang/ja.json @@ -15,6 +15,7 @@ "Overlay": "ページオーバーレイ", "PluginDescription": "実際の web サイト上のオーバーレイ分析データを参照してください。ユーザーが各リンクをクリックした回数が表示されます。注: 有効なトランジションプラグインが必要です。", "RedirectUrlError": "URL \"%s\" に対するページオーバーレイを開こうとしています。%s Piwik 設定のドメインが、すべてリンクに一致しません。", + "RedirectUrlErrorAdmin": "%s 設定で %s 追加の URL としてドメインを追加できます。", "RedirectUrlErrorUser": "管理者に、任意のドメインを追加 URL として追加するよう管理者に依頼してください。" } }
\ No newline at end of file diff --git a/plugins/Overlay/lang/ko.json b/plugins/Overlay/lang/ko.json index 4b761eaf5e..1c2ebbc404 100644 --- a/plugins/Overlay/lang/ko.json +++ b/plugins/Overlay/lang/ko.json @@ -13,6 +13,7 @@ "OneClick": "1회 클릭", "OpenFullScreen": "전체 화면 (사이드바 아님)으로 이동", "Overlay": "페이지 오버레이", + "PluginDescription": "당신의 웹사이트 분석 데이터를 오버레이 형식으로 볼 수 있습니다. 사용자가 얼마나 각 링크를 클릭했는지 확인해보세요. 참고: Transitions 플러그인이 활성화 되어 있어야 합니다.", "RedirectUrlError": "URL \"%s\"에 대한 페이지 오버레이를 열려고 시도합니다. %s은 Piwik 설정한 도메인과 어떤 링크도 일치하지 않습니다.", "RedirectUrlErrorAdmin": "당신은 %s설정%s에서 추가적인 URL로 도메인을 추가할 수 있습니다.", "RedirectUrlErrorUser": "도메인에 추가적인 URL을 추가하려면 관리자에게 문의하세요." diff --git a/plugins/Overlay/stylesheets/overlay.css b/plugins/Overlay/stylesheets/overlay.css index 4e30660775..a13dd67a46 100644 --- a/plugins/Overlay/stylesheets/overlay.css +++ b/plugins/Overlay/stylesheets/overlay.css @@ -35,12 +35,20 @@ a#overlayTitle .icon-help { margin: 20px 10px; } -#overlayLocation { +#overlayLocation, .overlaySegment { width: 200px; - margin: 0 0 30px 10px; font-size: 12px; } +#overlayLocation { + margin: 0 0 10px 10px; +} + +.overlaySegment { + margin: 0 0 30px 0; + word-break: break-all; +} + #overlayLoading { background: url(../../Morpheus/images/loading-blue.gif) no-repeat center 10px; width: 190px; diff --git a/plugins/Overlay/templates/index.twig b/plugins/Overlay/templates/index.twig index 10a0b40d76..effe5fbade 100644 --- a/plugins/Overlay/templates/index.twig +++ b/plugins/Overlay/templates/index.twig @@ -61,8 +61,8 @@ <script type="text/javascript"> broadcast._isInit = true; $(function () { - var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ rawDate }}'; - Piwik_Overlay.init(iframeSrc, '{{ idSite }}', '{{ period }}', '{{ rawDate }}'); + var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ rawDate }}&segment={{ segment }}'; + Piwik_Overlay.init(iframeSrc, '{{ idSite }}', '{{ period }}', '{{ rawDate }}', '{{ segment }}'); window.Piwik_Overlay_Translations = { domain: "{{ 'Overlay_Domain'|translate }}" diff --git a/plugins/Overlay/templates/index_noframe.twig b/plugins/Overlay/templates/index_noframe.twig index 0478715edb..f3c9e61579 100644 --- a/plugins/Overlay/templates/index_noframe.twig +++ b/plugins/Overlay/templates/index_noframe.twig @@ -6,7 +6,7 @@ <div id="overlayNoFrame"> <script type="text/javascript"> - var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ date }}'; + var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ date }}&segment={{ segment }}'; var locationParts = window.location.href.split('#'); if (locationParts.length > 1) { diff --git a/plugins/Overlay/templates/renderSidebar.twig b/plugins/Overlay/templates/renderSidebar.twig index 09d8cf0d40..36ff9c8ba1 100644 --- a/plugins/Overlay/templates/renderSidebar.twig +++ b/plugins/Overlay/templates/renderSidebar.twig @@ -8,6 +8,11 @@ </span> </div> + <div class="overlaySegment"> + <strong>{{ 'General_Segment'|translate }}:</strong> + <span>{{ segmentDescription }}</span> + </div> + {% if data|length > 0 %} <h2 class="overlayMainMetrics">{{ 'General_MainMetrics'|translate }}</h2> <ul class="overlayMetrics"> diff --git a/plugins/Overlay/templates/startOverlaySession.twig b/plugins/Overlay/templates/startOverlaySession.twig new file mode 100644 index 0000000000..b1db8ce5b2 --- /dev/null +++ b/plugins/Overlay/templates/startOverlaySession.twig @@ -0,0 +1,50 @@ +<html><head><title></title></head><body> +<script type="text/javascript"> + function handleProtocol(url) { + if ({% if isHttps %}true{% else %}false{% endif %}) { + return url.replace(/http:\/\//i, "https://"); + } else { + return url.replace(/https:\/\//i, "http://"); + } + } + + function removeUrlPrefix(url) { + return url.replace(/http(s)?:\/\/(www\.)?/i, ""); + } + + if (window.location.hash) { + var match = false; + + var urlToRedirect = window.location.hash.substr(1); + var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect); + + var knownUrls = {{ knownUrls|raw }}; + for (var i = 0; i < knownUrls.length; i++) { + var testUrl = removeUrlPrefix(knownUrls[i]); + if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) { + match = true; + if (navigator.appName == "Microsoft Internet Explorer") { + // internet explorer loses the referrer if we use window.location.href=X + var referLink = document.createElement("a"); + referLink.href = handleProtocol(urlToRedirect); + document.body.appendChild(referLink); + referLink.click(); + } else { + window.location.href = handleProtocol(urlToRedirect); + } + break; + } + } + + if (!match) { + var idSite = window.location.href.match(/idSite=([0-9]+)/i)[1]; + window.location.href = "index.php?module=Overlay&action=showErrorWrongDomain" + + "&idSite=" + idSite + + "&url=" + encodeURIComponent(urlToRedirect); + } + } + else { + window.location.href = handleProtocol("{{ mainUrl|e('js') }}"); + }; +</script> +</body></html>
\ No newline at end of file diff --git a/plugins/PrivacyManager/lang/cs.json b/plugins/PrivacyManager/lang/cs.json index 45881dd487..1677ed1252 100644 --- a/plugins/PrivacyManager/lang/cs.json +++ b/plugins/PrivacyManager/lang/cs.json @@ -2,7 +2,7 @@ "PrivacyManager": { "AnonymizeIpDescription": "Zvolte \"ano\", pokud nemá Piwik sledovat plně kvalifikované IP adresy.", "AnonymizeIpInlineHelp": "Skryje poslední byte IP adresy návštěvníka, aby souhlasila se zákony vaší země.", - "AnonymizeIpExtendedHelp": "Když uživatelé navštíví vaše stránky, Piwik neuloží jejich plnou IP adresu(jako %s), ale nejprve bude anonymizována (na %s). Anonymizace IP adres je jeden z požadavků práva na ochranu soukromí v některých zemí, jako je třeba Německo.", + "AnonymizeIpExtendedHelp": "Když uživatelé navštíví vaše stránky, Piwik neuloží jejich plnou IP adresu (jako %s), ale nejprve bude anonymizována (na %s). Anonymizace IP adres je jedním z požadavků práva na ochranu soukromí v některých zemí, jako je třeba Německo.", "AnonymizeIpMaskLengtDescription": "Zvolte, kolik bitů z návštěvníkovy IP adresy má být maskováno.", "AnonymizeIpMaskLength": "%s bitů - např. %s", "CannotLockSoDeleteLogActions": "Tabulka log_action nebude vyprázdněna: dejte Mysql uživateli %s privilegium LOCK TABLES.", diff --git a/plugins/PrivacyManager/lang/ja.json b/plugins/PrivacyManager/lang/ja.json index ed8f1a4bf7..aee8d4f898 100644 --- a/plugins/PrivacyManager/lang/ja.json +++ b/plugins/PrivacyManager/lang/ja.json @@ -13,6 +13,8 @@ "DeleteDataDescription": "データベースのサイズを小さく保つために、古い訪問者のログ、かつ\/または生成されたレポートを定期的に削除するようPiwikを設定できます。", "DeleteDataDescription2": "必要であれば、事前処理されたレポートは削除せず、ビジットとページビューとコンバージョンのログデータのみの削除もできます。あるいは、事前処理されたレポートを削除し、ログデータを保存することもできます。", "DeleteDataInterval": "古いデータを削除:毎", + "DeleteOldVisitorLogs": "古いビジターログを削除", + "DeleteOldArchivedReports": "古いアーカイブレポートを削除", "DeleteLogDescription2": "自動ログ削除を有効にする時は、データが失われないように、以前のすべての日次レポートが処理されていることを確認する必要があります。", "DeleteLogInfo": "以下のテーブルのログが削除されます: %s", "DeleteLogsConfirm": "あなたは、ログデータの削除を有効にしようとしています。古いログデータが削除され、レポートが既に作成されていない場合には、歴史的な過去の分析データを見ることはできません。あなたは本当にこれをしたいですか?", @@ -25,6 +27,7 @@ "DeleteReportsInfo2": "\"%s\"が有効でない場合は、要求されれば、古いレポートが自動的に再作成されます。", "DeleteReportsInfo3": "\"%s\" を有効にすると、データは永久に失われます。", "DeleteReportsOlderThan": "この月数より古いリポートを削除", + "DeleteSchedulingSettings": "古いデータの削除をスケジュールします。", "DeleteDataSettings": "古いビジターのログとリポートを削除", "DoNotTrack_Description": "「トラック(追跡)しない」機能は、ユーザーが訪問しないウェブサイト(分析サービス、広告ネットワーク、ソーシャルプラットフォームを含む)によるトラッキングのオプトアウトを可能にするためのテクノロジーとポリシーの提言です。", "DoNotTrack_Disable": "「トラック(追跡)しない」機能のサポートを無効にする", @@ -39,15 +42,18 @@ "GeolocationAnonymizeIpNote": "注) 位置情報探索機能は、匿名化された 1byte の場合とほぼ同じ結果になります。2byte 以上を使用すると、位置情報探索機能が不正確になります。", "GetPurgeEstimate": "パージ推定値を取得", "KeepBasicMetrics": "基本のメトリックスを保持(ビジット、ページビュー、直帰率、目標コンバージョン、eコマースコンバージョン等)", + "KeepDataFor": "すべてのデータを保持", "KeepReportSegments": "上記の保持されるデータについては、レポートのセグメントも保持する", "LastDelete": "最後に削除したのは", "LeastDaysInput": "日数は %s より大きい数を指定してください。", "LeastMonthsInput": "月数は %s より大きい数を指定してください。", "MenuPrivacySettings": "プライバシー", "NextDelete": "次回の削除まで", + "PluginDescription": "ユーザーのプライバシーを増強し、Piwik インスタンス プライバシーを地域の法律に準拠させます。", "PurgeNow": "今DBを削除する", "PurgeNowConfirm": "データベースから永久にデータを削除しようとしています。続けますか?", "PurgingData": "データを削除しています...", + "RecommendedForPrivacy": "プライバシー推奨", "ReportsDataSavedEstimate": "データベースサイズ", "SaveSettingsBeforePurge": "データ削除設定を変更しました。削除が開始される前に保存してください。", "SeeAlsoOurOfficialGuidePrivacy": "私達のオフィシャルガイドもご確認ください。%sWeb Analytics Privacy%s", diff --git a/plugins/PrivacyManager/lang/ko.json b/plugins/PrivacyManager/lang/ko.json index ff0c89f872..84987a600a 100644 --- a/plugins/PrivacyManager/lang/ko.json +++ b/plugins/PrivacyManager/lang/ko.json @@ -2,6 +2,7 @@ "PrivacyManager": { "AnonymizeIpDescription": "완전히 확인 된 IP 주소를 추적하지 않도록하려면 \"예\"를 선택합니다.", "AnonymizeIpInlineHelp": "개인이 설정한 내부 규칙 및 지침에 따라 방문자의 IP주소의 마지막 바이트를 숨깁니다.", + "AnonymizeIpExtendedHelp": "유저들이 웹사이트에 접속할 때, Piwik는 모든 IP 주소(예: %s)를 사용하지 않고 일부를 숨깁니다(예: %s). IP 주소 익명 처리는 독일과 같이 특정 국가에서 프라이버시 법률에 따라 요구되는 것 중 하나입니다.", "AnonymizeIpMaskLengtDescription": "방문자의 IP 주소에서 몇 바이트를 숨기려면 선택하세요.", "AnonymizeIpMaskLength": "%s 바이트 - 예) %s", "CannotLockSoDeleteLogActions": "log_action 테이블을 비울 수 없습니다: '%s' MYSQL 사용자에게 LOCK 테이블 권한을 부여하세요.", @@ -12,6 +13,8 @@ "DeleteDataDescription": "데이터베이스 크기를 작게 유지하기 위해 기존 방문자의 로그와 생성된 보고서를 정기적으로 삭제하도록 Piwik을 설정할 수 있습니다.", "DeleteDataDescription2": "필요한 경우, 사전 처리된 보고서는 삭제하지 않고, 방문과 페이지뷰 및 전환 로그 데이터만 삭제할 수 있습니다. 또는 사전 처리된 보고서를 제거하고 로그 데이터를 저장할 수 있습니다.", "DeleteDataInterval": "오래된 데이터 삭제", + "DeleteOldVisitorLogs": "오래된 방문자 로그 삭제", + "DeleteOldArchivedReports": "오래된 아카이브된 보고서 삭제", "DeleteLogDescription2": "자동으로 로그 삭제를 활성화할 때 데이터가 손실되지 않도록 모든 이전의 일일 보고서가 처리되고 있는지 확인해야합니다.", "DeleteLogInfo": "다음 테이블의 로그가 삭제됩니다: %s", "DeleteLogsConfirm": "당신은 로그 데이터의 삭제를 사용하려고합니다. 이전 로그 데이터가 삭제되고 보고서가 이미 생성되어 있지 않은 경우에는 과거 이력 분석 데이터를 볼 수 없습니다. 정말 실행할까요?", @@ -24,12 +27,13 @@ "DeleteReportsInfo2": "\"%s\"가 유효하지 않은 경우, 요구되는 경우, 이전 보고서가 자동으로 다시 만들어집니다.", "DeleteReportsInfo3": "\"%s\"를 사용하면 데이터가 영구적으로 손실됩니다.", "DeleteReportsOlderThan": "오래된 보고서 삭제", + "DeleteSchedulingSettings": "오래된 데이터 삭제 계획하기", "DeleteDataSettings": "오래된 방문자 기록 및 보고서 삭제", "DoNotTrack_Description": "\"추적 않함\"기능은 사용자가 방문한 웹사이트 (분석 서비스, 광고 네트워크, 소셜 플랫폼 등)에 의한 추적 차단을 가능하게하는 기술과 정책 제언입니다.", "DoNotTrack_Disable": "\"추적하지 않음\"기능 지원 비활성화", "DoNotTrack_Disabled": "Piwik은 현재 브라우저에서 \"추적당하고 싶지 않음\"이 지정된 경우에도 모든 방문자를 추적하고 있습니다.", "DoNotTrack_DisabledMoreInfo": "방문자의 프라이버시를 존중하고 \"추적하지 않기\"기능 지원을 활성화하는 것이 좋습니다.", - "DoNotTrack_Enable": "\"추적 않함\"기능 활성화", + "DoNotTrack_Enable": "\"추적하지 않음\"기능 활성화", "DoNotTrack_Enabled": "당신은 현재 사용자의 프라이버시를 존중합니다. 브라보!", "DoNotTrack_EnabledMoreInfo": "브라우저에서 사용자가 \"추적당하고 싶지 않음\"을 설정한 경우, (\"추적하지 않음\"기능이 활성화되어 있으면) Piwik 이러한 방문자를 추적하지 않습니다.", "DoNotTrack_SupportDNTPreference": "\"추적하지 않음\" 지원 기능 설정", @@ -38,19 +42,25 @@ "GeolocationAnonymizeIpNote": "참고: 위치 정보는 대략 1바이트로 익명으로 처리되는 것과 같은 결과를 얻습니다. 2바이트 또는 이상이면 위치 정보가 정확하지 않을 수 있습니다.", "GetPurgeEstimate": "비운 추정치 받기", "KeepBasicMetrics": "기본 지표 유지 (방문, 페이지뷰, 반송률, 목표전환, 전자상거래 전환 등)", + "KeepDataFor": "모든 데이터를 유지합니다:", "KeepReportSegments": "위 유지되는 데이터는 보고서의 세그먼트에도 유지", "LastDelete": "마지막으로 삭제한 것은", "LeastDaysInput": "일 수는 %s보다 커야합니다.", - "LeastMonthsInput": "달은 %s보다 커야합니다.", + "LeastMonthsInput": "개월은 %s보다 커야합니다.", "MenuPrivacySettings": "개인 정보 보호", "NextDelete": "다음 제거까지", + "PluginDescription": "유저 프라이버시를 높이고, 또한 프라이버시와 관련한 국내 법률을 따르도록 만드세요.", "PurgeNow": "지금 DB 삭제", "PurgeNowConfirm": "영구적으로 데이터베이스의 데이터를 삭제하려고합니다. 계속 하시겠습니까?", "PurgingData": "데이터를 삭제합니다...", + "RecommendedForPrivacy": "프라이버시를 위해 권장함", "ReportsDataSavedEstimate": "데이터베이스 크기", "SaveSettingsBeforePurge": "데이터 삭제 설정을 변경했습니다. 삭제가 시작되기 전에 저장하세요.", + "SeeAlsoOurOfficialGuidePrivacy": "공식 가이드인 %sWeb Analytics Privacy%s을 참고해주세요.", "Teaser": "이 페이지에서는 기존의 법률에 따라, Piwik이 개인에 맞도록 정의할 수 있습니다: %s 익명 방문자의 IP 주소 %s, %s 자동으로 데이터베이스에서 오래된 방문자의 로그를 삭제 %s, %s 웹사이트를 탈퇴하는 방법을 제공 %s에 따름.", "TeaserHeadline": "개인 정보 보호 설정", + "UseAnonymizedIpForVisitEnrichment": "또한, 방문이 많을 때 익명화된 IP 주소를 사용합니다.", + "UseAnonymizedIpForVisitEnrichmentNote": "IP 주소와 공급자를 통하여 방문자의 지역 정보를 알아내는 것 같은 플러그인은 방문자의 메타데이터를 향상시킵니다. 기본적으로 이런 플러그인은 익명화된 IP 주소를 사용합니다. 만약 '아니오'를 선택할 경우, 익명처리되지 않은 전체 IP 주소를 대신 사용하게 되어 프라이버시는 낮추지만 대신 데이터 정확도를 높일 것입니다.", "UseAnonymizeIp": "익명 방문자의 IP 주소", "UseDeleteLog": "정기적으로 데이터베이스에서 오래된 방문자의 로그를 삭제함", "UseDeleteReports": "정기적으로 데이터베이스에서 오래된 보고서를 삭제" diff --git a/plugins/PrivacyManager/lang/tr.json b/plugins/PrivacyManager/lang/tr.json index 22d6df66b6..a863ba7e52 100644 --- a/plugins/PrivacyManager/lang/tr.json +++ b/plugins/PrivacyManager/lang/tr.json @@ -1,6 +1,13 @@ { "PrivacyManager": { "AnonymizeIpInlineHelp": "Yerel gizlilik kurallarına uymak için ziyaretçi IP adreslerini gizleyin.", + "CurrentDBSize": "Güncel veritabanı boyutu", + "DeleteOldVisitorLogs": "Eski ziyaretçi kayıtlarını sil", + "DeleteOldArchivedReports": "Arşivlenen eski raporları sil", + "DeleteMaxRowsNoLimit": "Limit yok", + "DeleteDataSettings": "Eski ziyaretçi kayıtlarını ve raporları sil", + "MenuPrivacySettings": "Gizlilik", + "ReportsDataSavedEstimate": "Veritabanı boyutu", "TeaserHeadline": "Gizlilik Ayarları" } }
\ No newline at end of file diff --git a/plugins/Provider/lang/ja.json b/plugins/Provider/lang/ja.json index 8f34448bfc..4ac0948932 100644 --- a/plugins/Provider/lang/ja.json +++ b/plugins/Provider/lang/ja.json @@ -1,6 +1,7 @@ { "Provider": { "ColumnProvider": "プロバイダ", + "PluginDescription": "ビジターのインターネットサービスプロバイダを報告します", "ProviderReportDocumentation": "このリポートは、ウェブサイトにアクセスするビジターが使っているインターネットサービスプロバイダを示しています。詳細については、プロバイダ名をクリックしてください。 %s Piwik がビジターのプロバイダを判別できない場合は、IPとして表示されます。", "WidgetProviders": "プロバイダ", "ProviderReportFooter": "未知のプロバイダーとは IP アドレスが検索できなかったことを意味します。" diff --git a/plugins/Provider/lang/sl.json b/plugins/Provider/lang/sl.json index c13e93cdc9..8f5a09e046 100644 --- a/plugins/Provider/lang/sl.json +++ b/plugins/Provider/lang/sl.json @@ -1,6 +1,8 @@ { "Provider": { "ColumnProvider": "Ponudnik", + "PluginDescription": "Poročilo o obiskovalčevem ponudniku dostopa do internetnih storitev.", + "ProviderReportDocumentation": "To poročilo prikazuje ponudnike dostopa do internetnih storitve prek katerih so obiskovalci dostopali do vaše spletne strani. Za podrobnosti lahko kliknete na ime posameznega ponudnika. %s Če Piwik ne more določiti obiskovalčevega ponudnika, je le-ta naveden kot IP naslov.", "WidgetProviders": "Ponudniki", "ProviderReportFooter": "Neznan ponudnik pomeni, da IP številke ni bilo mogoče povezati s ponudnikom." } diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking -Subproject 35288ec165fc085360ec77b441dc25f51e04cd5 +Subproject 972fb6cc1953f7c21439c901fb87ab471eaf6b6 diff --git a/plugins/Referrers/API.php b/plugins/Referrers/API.php index d1c08ac20c..befd651fbe 100644 --- a/plugins/Referrers/API.php +++ b/plugins/Referrers/API.php @@ -230,14 +230,14 @@ class API extends \Piwik\Plugin\API $dataTable = Archive::createDataTableFromArchive(Archiver::SEARCH_ENGINES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, $flat); if ($flat) { - $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', __NAMESPACE__ . '\getSearchEngineUrlFromName')); - $dataTable->filter('MetadataCallbackAddMetadata', array('url', 'logo', __NAMESPACE__ . '\getSearchEngineLogoFromUrl')); + $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return SearchEngine::getInstance()->getUrlFromName($url); })); + $dataTable->filter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return SearchEngine::getInstance()->getLogoFromUrl($url); })); $dataTable->filterSubtables('Piwik\Plugins\Referrers\DataTable\Filter\KeywordsFromSearchEngineId', array($dataTable)); } else { $dataTable->filter('AddSegmentByLabel', array('referrerName')); $dataTable->queueFilter('PrependSegment', array('referrerType==search;')); - $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', __NAMESPACE__ . '\getSearchEngineUrlFromName')); - $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', __NAMESPACE__ . '\getSearchEngineLogoFromUrl')); + $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return SearchEngine::getInstance()->getUrlFromName($url); })); + $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return SearchEngine::getInstance()->getLogoFromUrl($url); })); } return $dataTable; @@ -309,9 +309,9 @@ class API extends \Piwik\Plugin\API { $dataTable = Archive::createDataTableFromArchive(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, false); - $dataTable->filter('ColumnCallbackDeleteRow', array('label', function ($url) { return !isSocialUrl($url); })); - $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', __NAMESPACE__ . '\getSocialMainUrl')); - $dataTable->filter('GroupBy', array('label', __NAMESPACE__ . '\getSocialNetworkFromDomain')); + $dataTable->filter('ColumnCallbackDeleteRow', array('label', function ($url) { return !Social::getInstance()->isSocialUrl($url); })); + $dataTable->filter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return Social::getInstance()->getMainUrl($url); })); + $dataTable->filter('GroupBy', array('label', function ($url) { return Social::getInstance()->getSocialNetworkFromDomain($url); })); $this->setSocialIdSubtables($dataTable); $this->removeSubtableMetadata($dataTable); @@ -320,7 +320,7 @@ class API extends \Piwik\Plugin\API $this->buildExpandedTableForFlattenGetSocials($idSite, $period, $date, $segment, $expanded, $dataTable); } - $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', __NAMESPACE__ . '\getSocialsLogoFromUrl')); + $dataTable->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return Social::getInstance()->getLogoFromUrl($url); })); return $dataTable; } @@ -334,7 +334,7 @@ class API extends \Piwik\Plugin\API * @param string $date * @param bool|string $segment * @param bool|int $idSubtable This ID does not reference a real DataTable record. Instead, it - * is the array index of an item in the /core/DataFiles/Socials.php file. + * is the array index of an item in the Socials list file. * The urls are filtered by the social network at this index. * If false, no filtering is done and every social URL is returned. * @return DataTable @@ -344,7 +344,7 @@ class API extends \Piwik\Plugin\API $dataTable = $this->getDataTable(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = true); // get the social network domain referred to by $idSubtable - $socialNetworks = Common::getSocialUrls(); + $socialNetworks = Social::getInstance()->getDefinitions(); $social = false; if ($idSubtable !== false) { @@ -362,7 +362,7 @@ class API extends \Piwik\Plugin\API $dataTable->filter( 'ColumnCallbackDeleteRow', array('label', - function ($url) use ($social) { return !isSocialUrl($url, $social); } + function ($url) use ($social) { return !Social::getInstance()->isSocialUrl($url, $social); } ) ); @@ -428,7 +428,7 @@ class API extends \Piwik\Plugin\API /** * Sets the subtable IDs for the DataTable returned by getSocial. * - * The IDs are int indexes into the array in /core/DataFiles/Socials.php. + * The IDs are int indexes into the array in of defined socials. * * @param DataTable $dataTable */ @@ -443,7 +443,7 @@ class API extends \Piwik\Plugin\API $socialName = $row->getColumn('label'); $i = 1; // start at one because idSubtable=0 is equivalent to idSubtable=false - foreach (Common::getSocialUrls() as $name) { + foreach (Social::getInstance()->getDefinitions() as $name) { if ($name == $socialName) { $row->setNonLoadedSubtableId($i); break; @@ -491,7 +491,7 @@ class API extends \Piwik\Plugin\API { $urlsTable = Archive::createDataTableFromArchive(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, $flat = true); $urlsTable->filter('ColumnCallbackDeleteRow', array('label', function ($url) { - return !isSocialUrl($url); + return !Social::getInstance()->isSocialUrl($url); })); $urlsTable = $urlsTable->mergeSubtables(); @@ -504,7 +504,7 @@ class API extends \Piwik\Plugin\API $rows = $urlsTable->getRows(); foreach ($rows as $id => $urlsTableRow) { $url = $urlsTableRow->getColumn('label'); - if (isSocialUrl($url, $social)) { + if (Social::getInstance()->isSocialUrl($url, $social)) { $newTable->addRow($urlsTableRow); $urlsTable->deleteRow($id); } diff --git a/plugins/Referrers/Columns/Base.php b/plugins/Referrers/Columns/Base.php index 78fe27516c..a4da79d389 100644 --- a/plugins/Referrers/Columns/Base.php +++ b/plugins/Referrers/Columns/Base.php @@ -11,6 +11,9 @@ namespace Piwik\Plugins\Referrers\Columns; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; +use Piwik\Plugins\Referrers\SearchEngine AS SearchEngineDetection; +use Piwik\Plugins\SitesManager\SiteUrls; +use Piwik\Tracker\Cache; use Piwik\Tracker\PageUrl; use Piwik\Tracker\Request; use Piwik\Tracker\Visit; @@ -111,6 +114,14 @@ abstract class Base extends VisitDimension if (!$referrerDetected && !empty($this->referrerHost)) { $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_WEBSITE; $this->nameReferrerAnalyzed = Common::mb_strtolower($this->referrerHost); + + $urlsByHost = $this->getCachedUrlsByHostAndIdSite(); + + $directEntry = new SiteUrls(); + $path = $directEntry->getPathMatchingUrl($this->referrerUrlParse, $urlsByHost); + if (!empty($path) && $path !== '/') { + $this->nameReferrerAnalyzed .= rtrim($path, '/'); + } } $referrerInformation = array( @@ -139,7 +150,7 @@ abstract class Base extends VisitDimension */ protected function detectReferrerSearchEngine() { - $searchEngineInformation = UrlHelper::extractSearchEngineInformationFromUrl($this->referrerUrl); + $searchEngineInformation = SearchEngineDetection::getInstance()->extractInformationFromUrl($this->referrerUrl); /** * Triggered when detecting the search engine of a referrer URL. @@ -241,6 +252,17 @@ abstract class Base extends VisitDimension return true; } + private function getCachedUrlsByHostAndIdSite() + { + $cache = Cache::getCacheGeneral(); + + if (!empty($cache['allUrlsByHostAndIdSite'])) { + return $cache['allUrlsByHostAndIdSite']; + } + + return array(); + } + /** * We have previously tried to detect the campaign variables in the URL * so at this stage, if the referrer host is the current host, @@ -250,20 +272,32 @@ abstract class Base extends VisitDimension */ protected function detectReferrerDirectEntry() { - if (!empty($this->referrerHost)) { - // is the referrer host the current host? - if (isset($this->currentUrlParse['host'])) { - $currentHost = Common::mb_strtolower($this->currentUrlParse['host'], 'UTF-8'); - if ($currentHost == Common::mb_strtolower($this->referrerHost, 'UTF-8')) { - $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; - return true; - } - } - if (Visit::isHostKnownAliasHost($this->referrerHost, $this->idsite)) { + if (empty($this->referrerHost)) { + return false; + } + + $urlsByHost = $this->getCachedUrlsByHostAndIdSite(); + + $directEntry = new SiteUrls(); + $matchingSites = $directEntry->getIdSitesMatchingUrl($this->referrerUrlParse, $urlsByHost); + + if (isset($matchingSites) && is_array($matchingSites) && in_array($this->idsite, $matchingSites)) { + $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; + return true; + } elseif (isset($matchingSites)) { + return false; + } + + // fallback logic if the referrer domain is not known to any site to not break BC + if (isset($this->currentUrlParse['host'])) { + // this might be actually buggy if first thing tracked is eg an outlink and referrer is from that site + $currentHost = Common::mb_strtolower($this->currentUrlParse['host']); + if ($currentHost == Common::mb_strtolower($this->referrerHost)) { $this->typeReferrerAnalyzed = Common::REFERRER_TYPE_DIRECT_ENTRY; return true; } } + return false; } @@ -277,7 +311,7 @@ abstract class Base extends VisitDimension // Set the Campaign keyword to the keyword found in the Referrer URL if any if (!empty($this->nameReferrerAnalyzed)) { - $referrerUrlInfo = UrlHelper::extractSearchEngineInformationFromUrl($this->referrerUrl); + $referrerUrlInfo = SearchEngineDetection::getInstance()->extractInformationFromUrl($this->referrerUrl); if (!empty($referrerUrlInfo['keywords'])) { $this->keywordReferrerAnalyzed = $referrerUrlInfo['keywords']; } @@ -448,7 +482,7 @@ abstract class Base extends VisitDimension protected function hasReferrerColumnChanged(Visitor $visitor, $information, $infoName) { - return Common::mb_strtolower($visitor->getVisitorColumn($infoName)) != $information[$infoName]; + return Common::mb_strtolower($visitor->getVisitorColumn($infoName)) != Common::mb_strtolower($information[$infoName]); } protected function doesLastActionHaveSameReferrer(Visitor $visitor, $referrerType) diff --git a/plugins/Referrers/Columns/Campaign.php b/plugins/Referrers/Columns/Campaign.php index ff2d5c2401..c29b622336 100644 --- a/plugins/Referrers/Columns/Campaign.php +++ b/plugins/Referrers/Columns/Campaign.php @@ -37,7 +37,7 @@ class Campaign extends Base /** * If we should create a new visit when the campaign changes, check if the campaign info changed and if so - * force the tracker to create a new visit. + * force the tracker to create a new visit.i * * @param Request $request * @param Visitor $visitor diff --git a/plugins/Referrers/Columns/Keyword.php b/plugins/Referrers/Columns/Keyword.php index 78605c9d94..b3bfc374b1 100644 --- a/plugins/Referrers/Columns/Keyword.php +++ b/plugins/Referrers/Columns/Keyword.php @@ -8,6 +8,7 @@ */ namespace Piwik\Plugins\Referrers\Columns; +use Piwik\Common; use Piwik\Piwik; use Piwik\Plugins\Referrers\Segment; use Piwik\Tracker\Request; @@ -44,7 +45,7 @@ class Keyword extends Base $information = $this->getReferrerInformationFromRequest($request); if (!empty($information['referer_keyword'])) { - return substr($information['referer_keyword'], 0, 255); + return Common::mb_substr($information['referer_keyword'], 0, 255); } return $information['referer_keyword']; diff --git a/plugins/Referrers/Columns/ReferrerName.php b/plugins/Referrers/Columns/ReferrerName.php index 7bdb0732a6..ded0a48f9e 100644 --- a/plugins/Referrers/Columns/ReferrerName.php +++ b/plugins/Referrers/Columns/ReferrerName.php @@ -8,6 +8,7 @@ */ namespace Piwik\Plugins\Referrers\Columns; +use Piwik\Common; use Piwik\Plugins\Referrers\Segment; use Piwik\Tracker\Request; use Piwik\Tracker\Visitor; @@ -38,10 +39,8 @@ class ReferrerName extends Base $information = $this->getReferrerInformationFromRequest($request); if (!empty($information['referer_name'])) { - - return substr($information['referer_name'], 0, 70); + return Common::mb_substr($information['referer_name'], 0, 70); } - return $information['referer_name']; } diff --git a/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php b/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php index 1688713a86..789a1b1a6a 100644 --- a/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php +++ b/plugins/Referrers/DataTable/Filter/KeywordsFromSearchEngineId.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Referrers\DataTable\Filter; use Piwik\DataTable\BaseFilter; use Piwik\DataTable\Row; use Piwik\DataTable; +use Piwik\Plugins\Referrers\SearchEngine; class KeywordsFromSearchEngineId extends BaseFilter { @@ -47,7 +48,7 @@ class KeywordsFromSearchEngineId extends BaseFilter if (!empty($subTableRow)) { $searchEngineUrl = $subTableRow->getMetadata('url'); - $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik\Plugins\Referrers\getSearchEngineUrlFromKeywordAndUrl', array($searchEngineUrl))); + $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($keyword, $url) { return SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword($url, $keyword); }, array($searchEngineUrl))); $table->queueFilter(function (DataTable $table) { $row = $table->getRowFromId(DataTable::ID_SUMMARY_ROW); if ($row) { diff --git a/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php b/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php index feab8636af..6d395abba0 100644 --- a/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php +++ b/plugins/Referrers/DataTable/Filter/SearchEnginesFromKeywordId.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\Referrers\DataTable\Filter; use Piwik\DataTable\BaseFilter; use Piwik\DataTable\Row; use Piwik\DataTable; +use Piwik\Plugins\Referrers\SearchEngine; class SearchEnginesFromKeywordId extends BaseFilter { @@ -44,14 +45,14 @@ class SearchEnginesFromKeywordId extends BaseFilter { $idSubtable = $this->idSubtable ? : $table->getId(); - $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', 'Piwik\Plugins\Referrers\getSearchEngineUrlFromName')); - $table->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', 'Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl')); + $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($url) { return SearchEngine::getInstance()->getUrlFromName($url); })); + $table->queueFilter('MetadataCallbackAddMetadata', array('url', 'logo', function ($url) { return SearchEngine::getInstance()->getLogoFromUrl($url); })); // get the keyword and create the URL to the search result page $rootRow = $this->firstLevelKeywordTable->getRowFromIdSubDataTable($idSubtable); if ($rootRow) { $keyword = $rootRow->getColumn('label'); - $table->queueFilter('MetadataCallbackReplace', array('url', 'Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword', array($keyword))); + $table->queueFilter('MetadataCallbackReplace', array('url', function ($url, $keyword) { return SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword($url, $keyword); }, array($keyword))); $table->queueFilter(function (DataTable $table) { $row = $table->getRowFromId(DataTable::ID_SUMMARY_ROW); if ($row) { diff --git a/plugins/Referrers/Referrers.php b/plugins/Referrers/Referrers.php index ce8667929b..d262c79224 100644 --- a/plugins/Referrers/Referrers.php +++ b/plugins/Referrers/Referrers.php @@ -12,6 +12,7 @@ use Piwik\ArchiveProcessor; use Piwik\Common; use Piwik\Piwik; use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable; +use Piwik\Plugins\SitesManager\SiteUrls; /** * @see plugins/Referrers/functions.php @@ -31,9 +32,18 @@ class Referrers extends \Piwik\Plugin 'Insights.addReportToOverview' => 'addReportToInsightsOverview', 'Live.getAllVisitorDetails' => 'extendVisitorDetails', 'Request.getRenamedModuleAndAction' => 'renameDeprecatedModuleAndAction', + 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral' ); } + public function setTrackerCacheGeneral(&$cacheContent) + { + $siteUrls = new SiteUrls(); + $urls = $siteUrls->getAllCachedSiteUrls(); + + return $cacheContent['allUrlsByHostAndIdSite'] = $siteUrls->groupUrlsByHost($urls); + } + public function renameDeprecatedModuleAndAction(&$module, &$action) { if($module == 'Referers') { diff --git a/plugins/Referrers/SearchEngine.php b/plugins/Referrers/SearchEngine.php new file mode 100644 index 0000000000..14009a9029 --- /dev/null +++ b/plugins/Referrers/SearchEngine.php @@ -0,0 +1,485 @@ +<?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\Cache; +use Piwik\Common; +use Piwik\Option; +use Piwik\Piwik; +use Piwik\Singleton; +use Piwik\UrlHelper; + +/** + * Contains methods to access search engine definition data. + */ +class SearchEngine extends Singleton +{ + const OPTION_STORAGE_NAME = 'SearchEngineDefinitions'; + + /** @var string location of definition file (relative to PIWIK_INCLUDE_PATH) */ + const DEFINITION_FILE = '/vendor/piwik/searchengine-and-social-list/SearchEngines.yml'; + + protected $definitionList = null; + + /** + * Returns list of search engines by URL + * + * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) ) + */ + public function getDefinitions() + { + $cache = Cache::getEagerCache(); + $cacheId = 'SearchEngine-' . self::OPTION_STORAGE_NAME; + + if ($cache->contains($cacheId)) { + $list = $cache->fetch($cacheId); + } else { + $list = $this->loadDefinitions(); + $cache->save($cacheId, $list); + } + + return $list; + } + + private function loadDefinitions() + { + if (empty($this->definitionList)) { + // Read first from the auto-updated list in database + $list = Option::get(self::OPTION_STORAGE_NAME); + + if ($list) { + $this->definitionList = unserialize(base64_decode($list)); + } else { + // Fallback to reading the bundled list + $yml = file_get_contents(PIWIK_INCLUDE_PATH . self::DEFINITION_FILE); + $this->definitionList = $this->loadYmlData($yml); + Option::set(self::OPTION_STORAGE_NAME, base64_encode(serialize($this->definitionList))); + } + } + + Piwik::postEvent('Referrer.addSearchEngineUrls', array(&$this->definitionList)); + + $this->convertLegacyDefinitions(); + + return $this->definitionList; + } + + /** + * @deprecated remove in 3.0 + */ + protected function convertLegacyDefinitions() + { + foreach ($this->definitionList as $url => $definition) { + if (!array_key_exists('name', $definition) && isset($definition[0]) && isset($definition[1])) { + $this->definitionList[$url] = array( + 'name' => $definition[0], + 'params' => $definition[1], + 'backlink' => @$definition[2], + 'charsets' => @$definition[3] + ); + } + } + + } + + /** + * Parses the given YML string and caches the resulting definitions + * + * @param string $yml + * @return array + */ + public function loadYmlData($yml) + { + $searchEngines = \Spyc::YAMLLoadString($yml); + + $this->definitionList = $this->transformData($searchEngines); + + return $this->definitionList; + } + + protected function transformData($searchEngines) + { + $urlToInfo = array(); + foreach ($searchEngines as $name => $info) { + foreach ($info as $urlDefinitions) { + foreach ($urlDefinitions['urls'] as $url) { + $searchEngineData = $urlDefinitions; + unset($searchEngineData['urls']); + $searchEngineData['name'] = $name; + $urlToInfo[$url] = $searchEngineData; + } + } + } + return $urlToInfo; + } + + /** + * Returns list of search engines by name + * + * @return array Array of ( searchEngineName => URL ) + */ + public function getNames() + { + $cacheId = 'SearchEngine.getSearchEngineNames'; + $cache = Cache::getTransientCache(); + $nameToUrl = $cache->fetch($cacheId); + + if (empty($nameToUrl)) { + $searchEngines = $this->getDefinitions(); + + $nameToUrl = array(); + foreach ($searchEngines as $url => $info) { + if (!isset($nameToUrl[$info['name']])) { + $nameToUrl[$info['name']] = $url; + } + } + $cache->save($cacheId, $nameToUrl); + } + + return $nameToUrl; + } + + /** + * Returns definitions for the given search engine host + * + * @param string $host + * @return array + */ + public function getDefinitionByHost($host) + { + $searchEngines = $this->getDefinitions(); + + if (!array_key_exists($host, $searchEngines)) { + return array(); + } + + return $searchEngines[$host]; + } + + /** + * Extracts a keyword from a raw not encoded URL. + * Will only extract keyword if a known search engine has been detected. + * Returns the keyword: + * - in UTF8: automatically converted from other charsets when applicable + * - strtolowered: "QUErY test!" will return "query test!" + * - trimmed: extra spaces before and after are removed + * + * The function returns false when a keyword couldn't be found. + * eg. if the url is "http://www.google.com/partners.html" this will return false, + * as the google keyword parameter couldn't be found. + * + * @see unit tests in /tests/core/Common.test.php + * @param string $referrerUrl URL referrer URL, eg. $_SERVER['HTTP_REFERER'] + * @return array|bool false if a keyword couldn't be extracted, + * or array( + * 'name' => 'Google', + * 'keywords' => 'my searched keywords') + */ + public function extractInformationFromUrl($referrerUrl) + { + $referrerParsed = @parse_url($referrerUrl); + $referrerHost = ''; + if (isset($referrerParsed['host'])) { + $referrerHost = $referrerParsed['host']; + } + if (empty($referrerHost)) { + return false; + } + // some search engines (eg. Bing Images) use the same domain + // as an existing search engine (eg. Bing), we must also use the url path + $referrerPath = ''; + if (isset($referrerParsed['path'])) { + $referrerPath = $referrerParsed['path']; + } + + $query = ''; + if (isset($referrerParsed['query'])) { + $query = $referrerParsed['query']; + } + + // Google Referrers URLs sometimes have the fragment which contains the keyword + if (!empty($referrerParsed['fragment'])) { + $query .= '&' . $referrerParsed['fragment']; + } + + $referrerHost = $this->getEngineHostFromUrl($referrerHost, $referrerPath, $query); + + if (empty($referrerHost)) { + return false; + } + + $definitions = $this->getDefinitionByHost($referrerHost); + + $searchEngineName = $definitions['name']; + $variableNames = $definitions['params']; + + $key = null; + if ($searchEngineName === 'Google Images' + || ($searchEngineName === 'Google' && strpos($referrerUrl, '/imgres') !== false) + ) { + if (strpos($query, '&prev') !== false) { + $query = urldecode(trim(UrlHelper::getParameterFromQueryString($query, 'prev'))); + $query = str_replace('&', '&', strstr($query, '?')); + } + $searchEngineName = 'Google Images'; + } elseif ($searchEngineName === 'Google' + && (strpos($query, '&as_') !== false || strpos($query, 'as_') === 0) + ) { + $keys = array(); + $key = UrlHelper::getParameterFromQueryString($query, 'as_q'); + if (!empty($key)) { + array_push($keys, $key); + } + $key = UrlHelper::getParameterFromQueryString($query, 'as_oq'); + if (!empty($key)) { + array_push($keys, str_replace('+', ' OR ', $key)); + } + $key = UrlHelper::getParameterFromQueryString($query, 'as_epq'); + if (!empty($key)) { + array_push($keys, "\"$key\""); + } + $key = UrlHelper::getParameterFromQueryString($query, 'as_eq'); + if (!empty($key)) { + array_push($keys, "-$key"); + } + $key = trim(urldecode(implode(' ', $keys))); + } + + if ($searchEngineName === 'Google') { + // top bar menu + $tbm = UrlHelper::getParameterFromQueryString($query, 'tbm'); + switch ($tbm) { + case 'isch': + $searchEngineName = 'Google Images'; + break; + case 'vid': + $searchEngineName = 'Google Video'; + break; + case 'shop': + $searchEngineName = 'Google Shopping'; + break; + } + } + + if (empty($key)) { + foreach ($variableNames as $variableName) { + if ($variableName[0] == '/') { + // regular expression match + if (preg_match($variableName, $referrerUrl, $matches)) { + $key = trim(urldecode($matches[1])); + break; + } + } else { + // search for keywords now &vname=keyword + $key = UrlHelper::getParameterFromQueryString($query, $variableName); + $key = trim(urldecode($key)); + + // Special cases: empty or no keywords + if (empty($key) + && ( + // Google search with no keyword + ($searchEngineName == 'Google' + && (empty($query) && (empty($referrerPath) || $referrerPath == '/') && empty($referrerParsed['fragment'])) + ) + + // Yahoo search with no keyword + || ($searchEngineName == 'Yahoo!' + && ($referrerParsed['host'] == 'r.search.yahoo.com') + ) + + // empty keyword parameter + || strpos($query, sprintf('&%s=', $variableName)) !== false + || strpos($query, sprintf('?%s=', $variableName)) !== false + + // search engines with no keyword + || $searchEngineName == 'Ixquick' + || $searchEngineName == 'Google Images' + || $searchEngineName == 'DuckDuckGo') + ) { + $key = false; + } + if (!empty($key) + || $key === false + ) { + break; + } + } + } + } + + // $key === false is the special case "No keyword provided" which is a Search engine match + if ($key === null || $key === '') { + return false; + } + + if (!empty($key)) { + if (!empty($definitions['charsets'])) { + $key = $this->convertCharset($key, $definitions['charsets']); + } + $key = Common::mb_strtolower($key); + } + + return array( + 'name' => $searchEngineName, + 'keywords' => $key, + ); + } + + protected function getEngineHostFromUrl($host, $path, $query) + { + $searchEngines = $this->getDefinitions(); + + $hostPattern = UrlHelper::getLossyUrl($host); + /* + * Try to get the best matching 'host' in definitions + * 1. check if host + path matches an definition + * 2. check if host only matches + * 3. check if host pattern + path matches + * 4. check if host pattern matches + * 5. special handling + */ + if (array_key_exists($host . $path, $searchEngines)) { + $host = $host . $path; + } elseif (array_key_exists($host, $searchEngines)) { + // no need to change host + } elseif (array_key_exists($hostPattern . $path, $searchEngines)) { + $host = $hostPattern . $path; + } elseif (array_key_exists($hostPattern, $searchEngines)) { + $host = $hostPattern; + } elseif (!array_key_exists($host, $searchEngines)) { + if (!strncmp($query, 'cx=partner-pub-', 15)) { + // Google custom search engine + $host = 'google.com/cse'; + } elseif (!strncmp($path, '/pemonitorhosted/ws/results/', 28)) { + // private-label search powered by InfoSpace Metasearch + $host = 'wsdsold.infospace.com'; + } elseif (strpos($host, '.images.search.yahoo.com') != false) { + // Yahoo! Images + $host = 'images.search.yahoo.com'; + } elseif (strpos($host, '.search.yahoo.com') != false) { + // Yahoo! + $host = 'search.yahoo.com'; + } else { + return false; + } + } + + return $host; + } + + /** + * Tries to convert the given string from one of the given charsets to UTF-8 + * @param string $string + * @param array $charsets + * @return string + */ + protected function convertCharset($string, $charsets) + { + if (function_exists('iconv') + && !empty($charsets) + ) { + $charset = $charsets[0]; + if (count($charsets) > 1 + && function_exists('mb_detect_encoding') + ) { + $charset = mb_detect_encoding($string, $charsets); + if ($charset === false) { + $charset = $charsets[0]; + } + } + + $newKey = @iconv($charset, 'UTF-8//IGNORE', $string); + if (!empty($newKey)) { + $string = $newKey; + } + } + + return $string; + } + + /** + * Return search engine URL by name + * + * @see core/DataFiles/SearchEnginges.php + * + * @param string $name + * @return string URL + */ + public function getUrlFromName($name) + { + $searchEngineNames = $this->getNames(); + if (isset($searchEngineNames[$name])) { + $url = 'http://' . $searchEngineNames[$name]; + } else { + $url = 'URL unknown!'; + } + return $url; + } + + /** + * Return search engine host in URL + * + * @param string $url + * @return string host + */ + private function getHostFromUrl($url) + { + if (strpos($url, '//')) { + $url = substr($url, strpos($url, '//') + 2); + } + if (($p = strpos($url, '/')) !== false) { + $url = substr($url, 0, $p); + } + return $url; + } + + + /** + * Return search engine logo path by URL + * + * @param string $url + * @return string path + * @see plugins/Referrers/images/searchEnginges/ + */ + public function getLogoFromUrl($url) + { + $pathInPiwik = 'plugins/Referrers/images/searchEngines/%s.png'; + $pathWithCode = sprintf($pathInPiwik, $this->getHostFromUrl($url)); + $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode; + if (file_exists($absolutePath)) { + return $pathWithCode; + } + return sprintf($pathInPiwik, 'xx'); + } + + /** + * Return search engine URL for URL and keyword + * + * @see core/DataFiles/SearchEnginges.php + * + * @param string $url Domain name, e.g., search.piwik.org + * @param string $keyword Keyword, e.g., web+analytics + * @return string URL, e.g., http://search.piwik.org/q=web+analytics + */ + public function getBackLinkFromUrlAndKeyword($url, $keyword) + { + if ($keyword === API::LABEL_KEYWORD_NOT_DEFINED) { + return 'http://piwik.org/faq/general/#faq_144'; + } + $keyword = urlencode($keyword); + $keyword = str_replace(urlencode('+'), urlencode(' '), $keyword); + $host = substr($url, strpos($url, '//') + 2); + $definition = $this->getDefinitionByHost($host); + if (empty($definition['backlink'])) { + return false; + } + $path = str_replace("{k}", $keyword, $definition['backlink']); + return $url . (substr($url, -1) != '/' ? '/' : '') . $path; + } +} diff --git a/plugins/Referrers/Social.php b/plugins/Referrers/Social.php new file mode 100644 index 0000000000..71badc3357 --- /dev/null +++ b/plugins/Referrers/Social.php @@ -0,0 +1,181 @@ +<?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\Cache; +use Piwik\Common; +use Piwik\Option; +use Piwik\Piwik; +use Piwik\Singleton; +use Piwik\UrlHelper; + +/** + * Contains methods to access search engine definition data. + */ +class Social extends Singleton +{ + const OPTION_STORAGE_NAME = 'SocialDefinitions'; + + /** @var string location of definition file (relative to PIWIK_INCLUDE_PATH) */ + const DEFINITION_FILE = '/vendor/piwik/searchengine-and-social-list/Socials.yml'; + + protected $definitionList = null; + + /** + * Returns list of search engines by URL + * + * @return array Array of ( URL => array( searchEngineName, keywordParameter, path, charset ) ) + */ + public function getDefinitions() + { + $cache = Cache::getEagerCache(); + $cacheId = 'Social-' . self::OPTION_STORAGE_NAME; + + if ($cache->contains($cacheId)) { + $list = $cache->fetch($cacheId); + } else { + $list = $this->loadDefinitions(); + $cache->save($cacheId, $list); + } + + return $list; + } + + private function loadDefinitions() + { + if ($this->definitionList === null) { + // Read first from the auto-updated list in database + $list = Option::get(self::OPTION_STORAGE_NAME); + + if ($list) { + $this->definitionList = unserialize(base64_decode($list)); + } else { + // Fallback to reading the bundled list + $yml = file_get_contents(PIWIK_INCLUDE_PATH . self::DEFINITION_FILE); + $this->definitionList = $this->loadYmlData($yml); + Option::set(self::OPTION_STORAGE_NAME, base64_encode(serialize($this->definitionList))); + } + } + + Piwik::postEvent('Referrer.addSocialUrls', array(&$this->definitionList)); + + return $this->definitionList; + } + + /** + * Parses the given YML string and caches the resulting definitions + * + * @param string $yml + * @return array + */ + public function loadYmlData($yml) + { + $searchEngines = \Spyc::YAMLLoadString($yml); + + $this->definitionList = $this->transformData($searchEngines); + + return $this->definitionList; + } + + protected function transformData($socials) + { + $urlToName = array(); + foreach ($socials as $name => $urls) { + foreach ($urls as $url) { + $urlToName[$url] = $name; + } + } + return $urlToName; + } + + /** + * Returns true if a URL belongs to a social network, false if otherwise. + * + * @param string $url The URL to check. + * @param string|bool $socialName The social network's name to check for, or false to check + * for any. + * @return bool + */ + public function isSocialUrl($url, $socialName = false) + { + foreach ($this->getDefinitions() as $domain => $name) { + + if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url) && ($socialName === false || $name == $socialName)) { + + return true; + } + } + + return false; + } + + + /** + * Get's social network name from URL. + * + * @param string $url + * @return string + */ + public function getSocialNetworkFromDomain($url) + { + foreach ($this->getDefinitions() as $domain => $name) { + + if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) { + + return $name; + } + } + + return Piwik::translate('General_Unknown'); + } + + /** + * Returns the main url of the social network the given url matches + * + * @param string $url + * + * @return string + */ + public function getMainUrl($url) + { + $social = $this->getSocialNetworkFromDomain($url); + foreach ($this->getDefinitions() as $domain => $name) { + + if ($name == $social) { + + return $domain; + } + } + return $url; + } + + + /** + * Return social network logo path by URL + * + * @param string $domain + * @return string path + * @see plugins/Referrers/images/socials/ + */ + public function getLogoFromUrl($domain) + { + $social = $this->getSocialNetworkFromDomain($domain); + $socialNetworks = $this->getDefinitions(); + + $filePattern = 'plugins/Referrers/images/socials/%s.png'; + + $socialDomains = array_keys($socialNetworks, $social); + foreach ($socialDomains as $domain) { + if (file_exists(PIWIK_INCLUDE_PATH . '/' . sprintf($filePattern, $domain))) { + return sprintf($filePattern, $domain); + } + } + + return sprintf($filePattern, 'xx'); + } +} diff --git a/plugins/Referrers/Tasks.php b/plugins/Referrers/Tasks.php new file mode 100644 index 0000000000..da9cad1321 --- /dev/null +++ b/plugins/Referrers/Tasks.php @@ -0,0 +1,54 @@ +<?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\Http; +use Piwik\Option; + +class Tasks extends \Piwik\Plugin\Tasks +{ + public function schedule() + { + $this->weekly('updateSearchEngines'); + $this->weekly('updateSocials'); + } + + /** + * Update the search engine definitions + * + * @see https://github.com/piwik/searchengine-and-social-list + */ + public function updateSearchEngines() + { + $url = 'https://raw.githubusercontent.com/piwik/searchengine-and-social-list/master/SearchEngines.yml'; + $list = Http::sendHttpRequest($url, 30); + $searchEngines = SearchEngine::getInstance()->loadYmlData($list); + if (count($searchEngines) < 200) { + return; + } + Option::set(SearchEngine::OPTION_STORAGE_NAME, base64_encode(serialize($searchEngines))); + } + + /** + * Update the social definitions + * + * @see https://github.com/piwik/searchengine-and-social-list + */ + public function updateSocials() + { + $url = 'https://raw.githubusercontent.com/piwik/searchengine-and-social-list/master/Socials.yml'; + $list = Http::sendHttpRequest($url, 30); + $socials = Social::getInstance()->loadYmlData($list); + if (count($socials) < 50) { + return; + } + Option::set(Social::OPTION_STORAGE_NAME, base64_encode(serialize($socials))); + } +}
\ No newline at end of file diff --git a/plugins/Referrers/Visitor.php b/plugins/Referrers/Visitor.php index 308c7b08f8..b10b9b953d 100644 --- a/plugins/Referrers/Visitor.php +++ b/plugins/Referrers/Visitor.php @@ -62,7 +62,7 @@ class Visitor ) { $refUrl = @parse_url($this->details['referer_url']); if (isset($refUrl['host'])) { - $url = getSearchEngineUrlFromUrlAndKeyword('http://google.com', $this->getKeyword()); + $url = SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword('http://google.com', $this->getKeyword()); $url = str_replace('google.com', $refUrl['host'], $url); return $url; @@ -109,7 +109,7 @@ class Visitor && !empty($this->details['referer_name']) ) { - return getSearchEngineUrlFromName($this->details['referer_name']); + return SearchEngine::getInstance()->getUrlFromName($this->details['referer_name']); } return null; @@ -121,7 +121,7 @@ class Visitor if (!is_null($searchEngineUrl)) { - return getSearchEngineLogoFromUrl($searchEngineUrl); + return SearchEngine::getInstance()->getLogoFromUrl($searchEngineUrl); } return null; diff --git a/plugins/Referrers/functions.php b/plugins/Referrers/functions.php index e0fee30833..c91ebc816d 100644 --- a/plugins/Referrers/functions.php +++ b/plugins/Referrers/functions.php @@ -28,194 +28,6 @@ function getPathFromUrl($url) } /** - * Returns the main url of the social network the given url matches - * - * @param string $url - * - * @return string - */ -function getSocialMainUrl($url) -{ - $social = getSocialNetworkFromDomain($url); - foreach (Common::getSocialUrls() as $domain => $name) { - - if ($name == $social) { - - return $domain; - } - } - return $url; -} - -/** - * Get's social network name from URL. - * - * @param string $url - * @return string - */ -function getSocialNetworkFromDomain($url) -{ - foreach (Common::getSocialUrls() as $domain => $name) { - - if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url)) { - - return $name; - } - } - - return Piwik::translate('General_Unknown'); -} - -/** - * Returns true if a URL belongs to a social network, false if otherwise. - * - * @param string $url The URL to check. - * @param string|bool $socialName The social network's name to check for, or false to check - * for any. - * @return bool - */ -function isSocialUrl($url, $socialName = false) -{ - foreach (Common::getSocialUrls() as $domain => $name) { - - if (preg_match('/(^|[\.\/])'.$domain.'([\.\/]|$)/', $url) && ($socialName === false || $name == $socialName)) { - - return true; - } - } - - return false; -} - -/** - * Return social network logo path by URL - * - * @param string $domain - * @return string path - * @see plugins/Referrers/images/socials/ - */ -function getSocialsLogoFromUrl($domain) -{ - $social = getSocialNetworkFromDomain($domain); - $socialNetworks = Common::getSocialUrls(); - - $filePattern = 'plugins/Referrers/images/socials/%s.png'; - - foreach ($socialNetworks as $domainKey => $name) { - if ($social == $socialNetworks[$domainKey] && file_exists(PIWIK_INCLUDE_PATH . '/' . sprintf($filePattern, $domainKey))) { - return sprintf($filePattern, $domainKey); - } - } - - return sprintf($filePattern, 'xx'); -} - -/** - * Return search engine URL by name - * - * @see core/DataFiles/SearchEnginges.php - * - * @param string $name - * @return string URL - */ -function getSearchEngineUrlFromName($name) -{ - $searchEngineNames = Common::getSearchEngineNames(); - if (isset($searchEngineNames[$name])) { - $url = 'http://' . $searchEngineNames[$name]; - } else { - $url = 'URL unknown!'; - } - return $url; -} - -/** - * Return search engine host in URL - * - * @param string $url - * @return string host - */ -function getSearchEngineHostFromUrl($url) -{ - if (strpos($url, '//')) { - $url = substr($url, strpos($url, '//') + 2); - } - if (($p = strpos($url, '/')) !== false) { - $url = substr($url, 0, $p); - } - return $url; -} - -/** - * Return search engine logo path by URL - * - * @param string $url - * @return string path - * @see plugins/Referrers/images/searchEnginges/ - */ -function getSearchEngineLogoFromUrl($url) -{ - $pathInPiwik = 'plugins/Referrers/images/searchEngines/%s.png'; - $pathWithCode = sprintf($pathInPiwik, getSearchEngineHostFromUrl($url)); - $absolutePath = PIWIK_INCLUDE_PATH . '/' . $pathWithCode; - if (file_exists($absolutePath)) { - return $pathWithCode; - } - return sprintf($pathInPiwik, 'xx'); -} - -/** - * Return search engine host and path in URL - * - * @param string $url - * @return string host - */ -function getSearchEngineHostPathFromUrl($url) -{ - $url = substr($url, strpos($url, '//') + 2); - return $url; -} - -/** - * Return search engine URL for URL and keyword - * - * @see core/DataFiles/SearchEnginges.php - * - * @param string $url Domain name, e.g., search.piwik.org - * @param string $keyword Keyword, e.g., web+analytics - * @return string URL, e.g., http://search.piwik.org/q=web+analytics - */ -function getSearchEngineUrlFromUrlAndKeyword($url, $keyword) -{ - if ($keyword === API::LABEL_KEYWORD_NOT_DEFINED) { - return 'http://piwik.org/faq/general/#faq_144'; - } - $searchEngineUrls = Common::getSearchEngineUrls(); - $keyword = urlencode($keyword); - $keyword = str_replace(urlencode('+'), urlencode(' '), $keyword); - $path = @$searchEngineUrls[getSearchEngineHostPathFromUrl($url)][2]; - if (empty($path)) { - return false; - } - $path = str_replace("{k}", $keyword, $path); - return $url . (substr($url, -1) != '/' ? '/' : '') . $path; -} - -/** - * Return search engine URL for keyword and URL - * - * @see \Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword - * - * @param string $keyword Keyword, e.g., web+analytics - * @param string $url Domain name, e.g., search.piwik.org - * @return string URL, e.g., http://search.piwik.org/q=web+analytics - */ -function getSearchEngineUrlFromKeywordAndUrl($keyword, $url) -{ - return getSearchEngineUrlFromUrlAndKeyword($url, $keyword); -} - -/** * Return translated referrer type * * @param string $label @@ -223,7 +35,6 @@ function getSearchEngineUrlFromKeywordAndUrl($keyword, $url) */ function getReferrerTypeLabel($label) { - $indexTranslation = ''; switch ($label) { case Common::REFERRER_TYPE_DIRECT_ENTRY: $indexTranslation = 'Referrers_DirectEntry'; diff --git a/plugins/Referrers/lang/cs.json b/plugins/Referrers/lang/cs.json index 740d25a38b..052a054161 100644 --- a/plugins/Referrers/lang/cs.json +++ b/plugins/Referrers/lang/cs.json @@ -3,9 +3,9 @@ "AllReferrersReportDocumentation": "Toto hlášení zobrazuje všechny referrery v jednom uceleném hlášení, kde zobrazuje všechny webové stránky, klíčová slova a kampaně, které vaši návštěvníci při hledání vašich webových stránek použili.", "Campaigns": "Kampaně", "CampaignsDocumentation": "Návštěvníci, kteří přišli na vaše webové stránky jako výsledek kampaně. %s Pro více informací si prohlédněte hlášení %s.", - "CampaignsReportDocumentation": "Toto hlášení zobrazuje, jaké kampaně přivedly návštěvníky na vaše stránky. %s Pro více informací o sledování kampaní si %spřečtěte dokumentaci kampaní na piwik.org%s.", + "CampaignsReportDocumentation": "Toto hlášení zobrazuje, které kampaně přivedly návštěvníky na vaše stránky. %s Pro více informací o sledování kampaní si %spřečtěte dokumentaci kampaní na piwik.org%s.", "ColumnCampaign": "Kampaň", - "ColumnSearchEngine": "Vyhledávací stroj", + "ColumnSearchEngine": "Vyhledávač", "ColumnSocial": "Sociální síť", "ColumnWebsite": "Web stránky", "ColumnWebsitePage": "Web stránka", @@ -15,7 +15,7 @@ "DistinctCampaigns": "jedinečné kampaně", "DistinctKeywords": "jedinečná klíčová slova", "DistinctSearchEngines": "jedinečné vyhledávače", - "DistinctWebsites": "jedinečná weby", + "DistinctWebsites": "jedinečné weby", "EvolutionDocumentation": "Toto je přehled referrerů, které vedly návštěvníky na vaše webové stránky.", "EvolutionDocumentationMoreInfo": "Pro více informací o rozdílných typech referrerů nahlédněte do dokumentace tabulky %s.", "Keywords": "Klíčová slova", @@ -26,13 +26,13 @@ "Referrers": "Doporučení", "ReferrersOverview": "Přehled referrerů", "ReferrerTypes": "Typy referrerů", - "SearchEngines": "Vyhledávacích strojů", + "SearchEngines": "Vyhledávače", "SearchEnginesDocumentation": "Návštěvník byl na vaše stránky odkázán vyhledávačem. %s Pro více informací si prohlédněte hlášení %s.", "SearchEnginesReportDocumentation": "Toto hlášení zobrazuje, které vyhledávače odkázali návštěvníky na vaše stránky. %s Kliknutím na řádek v tabulce zobrazíte, co uživatelé na daném vyhledávači hledali.", - "SocialFooterMessage": "Toto je podmnožina hlášení webových stránek zobrazeného vlevo. Filtruje ostatní stránky, takže můžete přímo porovnat referrery ze sociálních sítí.", + "SocialFooterMessage": "Toto je podmnožina hlášení zobrazeného vlevo. Filtruje ostatní stránky, takže můžete přímo porovnat referrery ze sociálních sítí.", "Socials": "Sociální sítě", "SocialsReportDocumentation": "Toto hlášení zobrazuje, jaké sociální sítě přivedly návštěvníky na vaše stránky. <br\/> Kliknutím na Kliknutím na řádek tabulky zobrazíte stránky sociální sítě, ze kterých návštěvníci přišli.", - "SubmenuSearchEngines": "Vyhledávače & klíčová slova", + "SubmenuSearchEngines": "Vyhledávače a klíčová slova", "SubmenuWebsites": "Web", "Type": "Typ refereru", "TypeCampaigns": "%s z kampaní", diff --git a/plugins/Referrers/lang/ko.json b/plugins/Referrers/lang/ko.json index f5f309c895..28c132386a 100644 --- a/plugins/Referrers/lang/ko.json +++ b/plugins/Referrers/lang/ko.json @@ -20,10 +20,12 @@ "EvolutionDocumentationMoreInfo": "다른 참조 유형에 대한 자세한 내용은 %s 테이블의 문서를 참조하세요.", "Keywords": "검색어", "KeywordsReportDocumentation": "이 보고서는 사용자가 웹사이트를 방문하는 데 사용하는 검색어를 나타냅니다. %s 테이블의 행을 클릭하면 검색어에 조회된 검색 엔진의 분포를 볼 수 있습니다.", + "PluginDescription": "리퍼터 데이터 보고서: 검색 엔진, 키워드, 웹사이트, 캠페인, 소셜 미디어, 직접 입력", "Referrer": "리퍼러", "ReferrerName": "리퍼러 이름", "Referrers": "참조", "ReferrersOverview": "리퍼러 개요", + "ReferrerTypes": "리퍼러 타입", "SearchEngines": "검색엔진", "SearchEnginesDocumentation": "검색 엔진을 참조하여 웹사이트로 접속한 방문자입니다. 상세 보고서는 %s 여기 %s에 있습니다.", "SearchEnginesReportDocumentation": "이 보고서는 어떤 검색 엔진으로 웹사이트에 사용자가 유입되었는 지를 보여줍니다. %s 테이블의 행을 클릭하면 사용자가 특정 검색 엔진을 사용하여 무엇을 찾고 있었는지를 확인할 수 있습니다.", @@ -47,6 +49,7 @@ "WidgetExternalWebsites": "외부 웹사이트 목록", "WidgetGetAll": "모든 리퍼러", "WidgetSocials": "소셜 네트워크의 목록", - "WidgetTopKeywordsForPages": "패지 URL의 상위 키워드" + "WidgetTopKeywordsForPages": "패지 URL의 상위 키워드", + "XPercentOfVisits": "%s 번" } }
\ No newline at end of file diff --git a/plugins/Referrers/tests/Integration/Columns/ReferrerNameTest.php b/plugins/Referrers/tests/Integration/Columns/ReferrerNameTest.php new file mode 100644 index 0000000000..db34a25c46 --- /dev/null +++ b/plugins/Referrers/tests/Integration/Columns/ReferrerNameTest.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\Referrers\tests\Integration\Columns; + +use Piwik\Common; +use Piwik\Plugins\Referrers\Columns\ReferrerName; +use Piwik\Plugins\Referrers\Columns\ReferrerType; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Cache; +use Piwik\Tracker\Request; +use Piwik\Tracker\Visit\VisitProperties; +use Piwik\Tracker\Visitor; + +/** + * @group Referrers + * @group ReferrerNameTest + * @group ReferrerName + * @group Plugins + */ +class ReferrerNameTest extends IntegrationTestCase +{ + /** + * @var ReferrerType + */ + private $referrerName; + private $idSite1 = 1; + private $idSite2 = 2; + private $idSite3 = 3; + + public function setUp() + { + parent::setUp(); + + Cache::clearCacheGeneral(); + + $date = '2012-01-01 00:00:00'; + $ecommerce = false; + + Fixture::createWebsite($date, $ecommerce, $name = 'test1', $url = 'http://piwik.org/foo/bar'); + Fixture::createWebsite($date, $ecommerce, $name = 'test2', $url = 'http://piwik.org/'); + Fixture::createWebsite($date, $ecommerce, $name = 'test3', $url = 'http://piwik.pro/'); + + $this->referrerName = new ReferrerName(); + } + + public function tearDown() + { + // clean up your test here if needed + Cache::clearCacheGeneral(); + + parent::tearDown(); + } + + /** + * @dataProvider getReferrerUrls + */ + public function test_onNewVisit_shouldDetectCorrectReferrerType($expectedType, $idSite, $url, $referrerUrl) + { + $request = $this->getRequest(array('idsite' => $idSite, 'url' => $url, 'urlref' => $referrerUrl)); + $type = $this->referrerName->onNewVisit($request, $this->getNewVisitor(), $action = null); + + $this->assertSame($expectedType, $type); + } + + public function getReferrerUrls() + { + $url = 'http://piwik.org/foo/bar'; + $referrer = 'http://piwik.org'; + + $directEntryReferrerName = ''; + + return array( + // domain matches but path does not match for idsite1 + array('piwik.org', $this->idSite1, $url, $referrer), + array('piwik.org', $this->idSite1, $url, $referrer . '/'), + // idSite2 matches any piwik.org path so this is a direct entry for it + array($directEntryReferrerName, $this->idSite2, $url, $referrer), + array($directEntryReferrerName, $this->idSite2, $url, $referrer . '/'), + // idSite3 has different domain so it is coming from different website + array('piwik.org', $this->idSite3, $url, $referrer), + array('piwik.org', $this->idSite3, $url, $referrer . '/'), + + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/foo/bar/baz'), + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/foo/bar/baz/'), + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/foo/bar/baz?x=5'), + array($directEntryReferrerName, $this->idSite1, $url, $referrer . '/fOo/BaR/baz?x=5'), + // /not/xyz belongs to different website + array('piwik.org', $this->idSite1, $url, $referrer . '/not/xyz'), + array($directEntryReferrerName, $this->idSite2, $url, $referrer . '/not/xyz'), + + // /foo/bar/baz belongs to different website + array('piwik.org/foo/bar', $this->idSite2, $url, $referrer . '/foo/bar/baz'), + array('piwik.org/foo/bar', $this->idSite3, $url, $referrer . '/foo/bar'), + array('piwik.org/foo/bar', $this->idSite3, $url, $referrer . '/fOo/BaR'), + + // should detect campaign independent of domain / path + array('test', $this->idSite1, $url . '?pk_campaign=test', $referrer), + array('testfoobar', $this->idSite2, $url . '?pk_campaign=testfoobar', $referrer), + array('test', $this->idSite3, $url . '?pk_campaign=test', $referrer), + + array('Google', $this->idSite3, $url, 'http://google.com/search?q=piwik'), + + // testing case for backwards compatibility where url has same domain as urlref but the domain is not known to any website + array($directEntryReferrerName, $this->idSite3, 'http://example.com/foo', 'http://example.com/bar'), + array($directEntryReferrerName, $this->idSite3, 'http://example.com/foo', 'http://example.com'), + array($directEntryReferrerName, $this->idSite3, 'http://example.com', 'http://example.com/bar'), + + // testing case where domain of referrer is not known to any site but neither is the URL, url != urlref + array('example.com', $this->idSite3, 'http://example.org', 'http://example.com/bar'), + ); + } + + private function getRequest($params) + { + return new Request($params); + } + + private function getNewVisitor() + { + return new Visitor(new VisitProperties()); + } + +} diff --git a/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.php new file mode 100644 index 0000000000..5925dbc5c0 --- /dev/null +++ b/plugins/Referrers/tests/Integration/Columns/ReferrerTypeTest.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\Referrers\tests\Integration\Columns; + +use Piwik\Common; +use Piwik\Plugins\Referrers\Columns\ReferrerType; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Cache; +use Piwik\Tracker\Request; +use Piwik\Tracker\Visit\VisitProperties; +use Piwik\Tracker\Visitor; + +/** + * @group Referrers + * @group ReferrerTypeTest + * @group ReferrerType + * @group Plugins + */ +class ReferrerTypeTest extends IntegrationTestCase +{ + /** + * @var ReferrerType + */ + private $referrerType; + private $idSite1 = 1; + private $idSite2 = 2; + private $idSite3 = 3; + + public function setUp() + { + parent::setUp(); + + Cache::clearCacheGeneral(); + + $date = '2012-01-01 00:00:00'; + $ecommerce = false; + + Fixture::createWebsite($date, $ecommerce, $name = 'test1', $url = 'http://piwik.org/foo/bar'); + Fixture::createWebsite($date, $ecommerce, $name = 'test2', $url = 'http://piwik.org/'); + Fixture::createWebsite($date, $ecommerce, $name = 'test3', $url = 'http://piwik.pro/'); + + $this->referrerType = new ReferrerType(); + } + + public function tearDown() + { + // clean up your test here if needed + Cache::clearCacheGeneral(); + + parent::tearDown(); + } + + /** + * @dataProvider getReferrerUrls + */ + public function test_onNewVisit_shouldDetectCorrectReferrerType($expectedType, $idSite, $url, $referrerUrl) + { + $request = $this->getRequest(array('idsite' => $idSite, 'url' => $url, 'urlref' => $referrerUrl)); + $type = $this->referrerType->onNewVisit($request, $this->getNewVisitor(), $action = null); + + $this->assertSame($expectedType, $type); + } + + public function getReferrerUrls() + { + $url = 'http://piwik.org/foo/bar'; + $referrer = 'http://piwik.org'; + + // $expectedType, $idSite, $url, $referrerUrl + return array( + // domain matches but path does not match for idsite1 + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer), + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer . '/'), + // idSite2 matches any piwik.org path so this is a direct entry for it + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer . '/'), + // idSite3 has different domain so it is coming from different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer), + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/'), + + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz/'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite1, $url, $referrer . '/foo/bar/baz?x=5'), + // /not/xyz belongs to different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite1, $url, $referrer . '/not/xyz'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite2, $url, $referrer . '/not/xyz'), + + // /foo/bar/baz belongs to different website + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite2, $url, $referrer . '/foo/bar/baz'), + + // website as it is from different domain anyway + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, $url, $referrer . '/foo/bar/baz'), + + // should detect campaign independent of domain / path + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite1, $url . '?pk_campaign=test', $referrer), + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite2, $url . '?pk_campaign=test', $referrer), + array(Common::REFERRER_TYPE_CAMPAIGN, $this->idSite3, $url . '?pk_campaign=test', $referrer), + + array(Common::REFERRER_TYPE_SEARCH_ENGINE, $this->idSite3, $url, 'http://google.com/search?q=piwik'), + + // testing case for backwards compatibility where url has same domain as urlref but the domain is not known to any website + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com/foo', 'http://example.com/bar'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com/foo', 'http://example.com'), + array(Common::REFERRER_TYPE_DIRECT_ENTRY, $this->idSite3, 'http://example.com', 'http://example.com/bar'), + + // testing case where domain of referrer is not known to any site but neither is the URL, url != urlref + array(Common::REFERRER_TYPE_WEBSITE, $this->idSite3, 'http://example.org', 'http://example.com/bar'), + ); + } + + private function getRequest($params) + { + return new Request($params); + } + + private function getNewVisitor() + { + return new Visitor(new VisitProperties()); + } + +} diff --git a/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml b/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml index edc1bd40ff..58601dc41b 100644 --- a/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml +++ b/plugins/Referrers/tests/System/expected/test_Referrers_getReferrerType__API.getProcessedReport_day.xml @@ -874,7 +874,7 @@ <result prettyDate="Tuesday, February 2, 2010"/> <result prettyDate="Wednesday, February 3, 2010"/> <result prettyDate="Thursday, February 4, 2010"> - <row> + <row> <idsubdatatable>2</idsubdatatable> </row> <row> diff --git a/plugins/Referrers/tests/Unit/ReferrersTest.php b/plugins/Referrers/tests/Unit/ReferrersTest.php index 909e6bf65e..e5dea866e9 100644 --- a/plugins/Referrers/tests/Unit/ReferrersTest.php +++ b/plugins/Referrers/tests/Unit/ReferrersTest.php @@ -11,217 +11,15 @@ namespace Piwik\Plugins\Referrers\tests; use Piwik\DataTable; use Piwik\DataTable\Row; use Piwik\Period; +use Piwik\Plugins\Referrers\SearchEngine; require_once PIWIK_INCLUDE_PATH . '/plugins/Referrers/Referrers.php'; +/** + * @group Plugin + */ class ReferrersTest extends \PHPUnit_Framework_TestCase { - /** - * Dataprovider serving all search engine data - */ - public function getSearchEngines() - { - include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php'; - - $searchEngines = array(); - foreach ($GLOBALS['Piwik_SearchEngines'] as $url => $searchEngine) { - $searchEngines[] = array($url, $searchEngine); - } - return $searchEngines; - } - - /** - * search engine has at least one keyword - * - * @group Plugins - * - * @dataProvider getSearchEngines - */ - public function testMissingSearchEngineKeyword($url, $searchEngine) - { - // Get list of search engines and first appearing URL - static $searchEngines = array(); - - $name = parse_url('http://' . $url); - if (!array_key_exists($searchEngine[0], $searchEngines)) { - $searchEngines[$searchEngine[0]] = $url; - - $this->assertTrue(!empty($searchEngine[1]), $name['host']); - } - } - - /** - * search engine is defined in DataFiles/SearchEngines.php but there's no favicon - * - * @group Plugins - * - * @dataProvider getSearchEngines - */ - public function testMissingSearchEngineIcons($url, $searchEngine) - { - // Get list of existing favicons - $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); - - // Get list of search engines and first appearing URL - static $searchEngines = array(); - - $name = parse_url('http://' . $url); - if (!array_key_exists($searchEngine[0], $searchEngines)) { - $searchEngines[$searchEngine[0]] = $url; - - $this->assertTrue(in_array($name['host'] . '.png', $favicons), $name['host']); - } - } - - /** - * favicon exists but there's no corresponding search engine defined in DataFiles/SearchEngines.php - * - * @group Plugins - */ - public function testObsoleteSearchEngineIcons() - { - include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php'; - - // Get list of search engines and first appearing URL - $searchEngines = array(); - foreach ($GLOBALS['Piwik_SearchEngines'] as $url => $searchEngine) { - $name = parse_url('http://' . $url); - if (!array_key_exists($name['host'], $searchEngines)) { - $searchEngines[$name['host']] = true; - } - } - - // Get list of existing favicons - $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); - foreach ($favicons as $name) { - if ($name[0] == '.' || strpos($name, 'xx.') === 0) { - continue; - } - - $host = substr($name, 0, -4); - $this->assertTrue(array_key_exists($host, $searchEngines), $host); - } - } - - /** - * get search engine host from url - * - * @group Plugins - */ - public function testGetSearchEngineHostFromUrl() - { - $data = array( - 'http://www.google.com/cse' => array('www.google.com', 'www.google.com/cse'), - 'http://www.google.com' => array('www.google.com', 'www.google.com'), - ); - - foreach ($data as $url => $expected) { - $this->assertEquals($expected[0], \Piwik\Plugins\Referrers\getSearchEngineHostFromUrl($url)); - $this->assertEquals($expected[1], \Piwik\Plugins\Referrers\getSearchEngineHostPathFromUrl($url)); - } - } - - /** - * Dataprovider for testGetSearchEngineUrlFromUrlAndKeyword - */ - public function getSearchEngineUrlFromUrlAndKeywordTestData() - { - return array( - array('http://apollo.lv/portal/search/', 'piwik', 'http://apollo.lv/portal/search/?cof=FORID%3A11&q=piwik&search_where=www'), - array('http://bing.com/images/search', 'piwik', 'http://bing.com/images/search/?q=piwik'), - array('http://google.com', 'piwik', 'http://google.com/search?q=piwik'), - ); - } - - /** - * get search engine url from name and keyword - * - * @group Plugins - * - * @dataProvider getSearchEngineUrlFromUrlAndKeywordTestData - */ - public function testGetSearchEngineUrlFromUrlAndKeyword($url, $keyword, $expected) - { - include PIWIK_PATH_TEST_TO_ROOT . '/core/DataFiles/SearchEngines.php'; - $this->assertEquals($expected, \Piwik\Plugins\Referrers\getSearchEngineUrlFromUrlAndKeyword($url, $keyword)); - } - - /** - * Dataprovider for getSocialNetworkFromDomainTestData - */ - public function getSocialNetworkFromDomainTestData() - { - return array( - array('http://www.facebook.com', 'Facebook'), - array('http://www.facebook.com/piwik', 'Facebook'), - array('http://m.facebook.com', 'Facebook'), - array('https://m.facebook.com', 'Facebook'), - array('m.facebook.com', 'Facebook'), - array('http://lastfm.com.tr', 'Last.fm'), - array('http://t.co/test', 'Twitter'), - array('http://xxt.co/test', \Piwik\Piwik::translate('General_Unknown')), - array('asdfasdfadsf.com', \Piwik\Piwik::translate('General_Unknown')), - array('http://xwayn.com', \Piwik\Piwik::translate('General_Unknown')), - array('http://live.com/test', \Piwik\Piwik::translate('General_Unknown')), - ); - } - - /** - * @group Plugins - * - * @dataProvider getSocialNetworkFromDomainTestData - */ - public function testGetSocialNetworkFromDomain($url, $expected) - { - include PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - $this->assertEquals($expected, \Piwik\Plugins\Referrers\getSocialNetworkFromDomain($url)); - } - - public function getSocialsLogoFromUrlTestData() - { - return array( - array('http://www.facebook.com', 'facebook.com.png'), - array('www.facebook.com', 'facebook.com.png',), - array('http://lastfm.com.tr', 'last.fm.png'), - array('http://asdfasdf.org/test', 'xx.png'), - array('http://www.google.com', 'xx.png'), - ); - } - - /** - * @group Plugins - * - * @dataProvider getSocialsLogoFromUrlTestData - */ - public function testGetSocialsLogoFromUrl($url, $expected) - { - include PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - $this->assertContains($expected, \Piwik\Plugins\Referrers\getSocialsLogoFromUrl($url)); - } - - public function isSocialUrlTestData() - { - return array( - array('http://www.facebook.com', 'Facebook', true), - array('http://www.facebook.com', 'Twitter', false), - array('http://m.facebook.com', false, true), - array('http://lastfm.com.tr', 'Last.fm', true), - array('http://asdfasdf.org/test', false, false), - array('http://asdfasdf.com/test', 'Facebook', false), - ); - } - - /** - * @group Plugins - * - * @dataProvider isSocialUrlTestData - */ - public function testIsSocialUrl($url, $assumedSocial, $expected) - { - include PIWIK_INCLUDE_PATH . '/core/DataFiles/Socials.php'; - $this->assertEquals($expected, \Piwik\Plugins\Referrers\isSocialUrl($url, $assumedSocial)); - } - public function removeUrlProtocolTestData() { return array( diff --git a/plugins/Referrers/tests/Unit/SearchEngineTest.php b/plugins/Referrers/tests/Unit/SearchEngineTest.php new file mode 100644 index 0000000000..e9f0c926ea --- /dev/null +++ b/plugins/Referrers/tests/Unit/SearchEngineTest.php @@ -0,0 +1,173 @@ +<?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\tests; + +use Piwik\Plugins\Referrers\SearchEngine; +use Spyc; + +/** + * @group SearchEngine + */ +class SearchEngineTest extends \PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + // inject definitions to avoid database usage + $yml = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . SearchEngine::DEFINITION_FILE); + SearchEngine::getInstance()->loadYmlData($yml); + parent::setUpBeforeClass(); + } + + public function getSearchEngineUrls() + { + return Spyc::YAMLLoad(PIWIK_PATH_TEST_TO_ROOT .'/tests/resources/extractSearchEngineInformationFromUrlTests.yml'); + } + + /** + * @dataProvider getSearchEngineUrls + */ + public function testExtractInformationFromUrl($url, $engine, $keywords) + { + $returnedValue = SearchEngine::getInstance()->extractInformationFromUrl($url); + + $expectedValue = false; + + if (!empty($engine)) { + $expectedValue = array('name' => $engine, 'keywords' => $keywords); + } + + $this->assertEquals($expectedValue, $returnedValue); + } + + public function testSearchEnginesDefinedCorrectly() + { + $searchEngines = array(); + foreach (SearchEngine::getInstance()->getDefinitions() as $host => $info) { + if (isset($info['backlink']) && $info['backlink'] !== false) { + $this->assertTrue(strrpos($info['backlink'], "{k}") !== false, $host . " search URL is not defined correctly, must contain the macro {k}"); + } + + if (!array_key_exists($info['name'], $searchEngines)) { + $searchEngines[$info['name']] = true; + + $this->assertTrue(strpos($host, '{}') === false, $host . " search URL is the master record and should not contain {}"); + } + + if (isset($info['charsets']) && $info['charsets'] !== false) { + $this->assertTrue(is_array($info['charsets']) || is_string($info['charsets']), $host . ' charsets must be either a string or an array'); + + if (is_string($info['charsets'])) { + $this->assertTrue(trim($info['charsets']) !== '', $host . ' charsets cannot be an empty string'); + $this->assertTrue(strpos($info['charsets'], ' ') === false, $host . ' charsets cannot contain spaces'); + + } + + if (is_array($info['charsets'])) { + $this->assertTrue(count($info['charsets']) > 0, $host . ' charsets cannot be an empty array'); + $this->assertTrue(strpos(serialize($info['charsets']), '""') === false, $host . ' charsets in array cannot be empty stringss'); + $this->assertTrue(strpos(serialize($info['charsets']), ' ') === false, $host . ' charsets in array cannot contain spaces'); + } + } + } + } + + /** + * Dataprovider for testGetBackLinkFromUrlAndKeyword + */ + public function getBackLinkFromUrlAndKeywordTestData() + { + return array( + array('http://apollo.lv/portal/search/', 'piwik', 'http://apollo.lv/portal/search/?cof=FORID%3A11&q=piwik&search_where=www'), + array('http://bing.com/images/search', 'piwik', 'http://bing.com/images/search/?q=piwik'), + array('http://google.com', 'piwik', 'http://google.com/search?q=piwik'), + ); + } + + /** + * get search engine url from name and keyword + * + * @dataProvider getBackLinkFromUrlAndKeywordTestData + */ + public function testGetBackLinkFromUrlAndKeyword($url, $keyword, $expected) + { + $this->assertEquals($expected, SearchEngine::getInstance()->getBackLinkFromUrlAndKeyword($url, $keyword)); + } + + /** + * Dataprovider serving all search engine data + */ + public function getAllSearchEngines() + { + $yml = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . SearchEngine::DEFINITION_FILE); + SearchEngine::getInstance()->loadYmlData($yml); + $searchEngines = array(); + foreach (SearchEngine::getInstance()->getDefinitions() as $url => $searchEngine) { + $searchEngines[] = array($url, $searchEngine); + } + return $searchEngines; + } + + /** + * search engine has at least one keyword + * + * @dataProvider getAllSearchEngines + */ + public function testMissingSearchEngineKeyword($url, $searchEngine) + { + $name = parse_url('http://' . $url); + $this->assertTrue(!empty($searchEngine['params']), $name['host']); + } + + /** + * search engine is defined but there's no favicon + * + * @dataProvider getAllSearchEngines + */ + public function testMissingSearchEngineIcons($url, $searchEngine) + { + // Get list of existing favicons + $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); + + // Get list of search engines and first appearing URL + static $searchEngines = array(); + + $name = parse_url('http://' . $url); + if (!array_key_exists($searchEngine['name'], $searchEngines)) { + $searchEngines[$searchEngine['name']] = $url; + + $this->assertTrue(in_array($name['host'] . '.png', $favicons), $name['host']); + } + } + + /** + * favicon exists but there's no corresponding search engine defined + */ + public function testObsoleteSearchEngineIcons() + { + // Get list of search engines and first appearing URL + $searchEngines = array(); + foreach (SearchEngine::getInstance()->getDefinitions() as $url => $searchEngine) { + $name = parse_url('http://' . $url); + if (!array_key_exists($name['host'], $searchEngines)) { + $searchEngines[$name['host']] = true; + } + } + + // Get list of existing favicons + $favicons = scandir(PIWIK_PATH_TEST_TO_ROOT . '/plugins/Referrers/images/searchEngines/'); + foreach ($favicons as $name) { + if ($name[0] == '.' || strpos($name, 'xx.') === 0) { + continue; + } + + $host = substr($name, 0, -4); + $this->assertTrue(array_key_exists($host, $searchEngines), $host); + } + } +}
\ No newline at end of file diff --git a/plugins/Referrers/tests/Unit/SocialTest.php b/plugins/Referrers/tests/Unit/SocialTest.php new file mode 100644 index 0000000000..8e8f35e0f9 --- /dev/null +++ b/plugins/Referrers/tests/Unit/SocialTest.php @@ -0,0 +1,97 @@ +<?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\tests; + +use Piwik\Plugins\Referrers\Social; +use Spyc; + +/** + * @group Social + * @group Plugins + */ +class SocialTest extends \PHPUnit_Framework_TestCase +{ + public static function setUpBeforeClass() + { + // inject definitions to avoid database usage + $yml = file_get_contents(PIWIK_PATH_TEST_TO_ROOT . Social::DEFINITION_FILE); + Social::getInstance()->loadYmlData($yml); + parent::setUpBeforeClass(); + } + + public function isSocialUrlTestData() + { + return array( + array('http://www.facebook.com', 'Facebook', true), + array('http://www.facebook.com', 'Twitter', false), + array('http://m.facebook.com', false, true), + array('http://lastfm.com.tr', 'Last.fm', true), + array('http://asdfasdf.org/test', false, false), + array('http://asdfasdf.com/test', 'Facebook', false), + ); + } + + /** + * @dataProvider isSocialUrlTestData + */ + public function testIsSocialUrl($url, $assumedSocial, $expected) + { + $this->assertEquals($expected, Social::getInstance()->isSocialUrl($url, $assumedSocial)); + } + + + /** + * Dataprovider for getSocialNetworkFromDomainTestData + */ + public function getSocialNetworkFromDomainTestData() + { + return array( + array('http://www.facebook.com', 'Facebook'), + array('http://www.facebook.com/piwik', 'Facebook'), + array('http://m.facebook.com', 'Facebook'), + array('https://m.facebook.com', 'Facebook'), + array('m.facebook.com', 'Facebook'), + array('http://lastfm.com.tr', 'Last.fm'), + array('http://t.co/test', 'Twitter'), + array('http://xxt.co/test', \Piwik\Piwik::translate('General_Unknown')), + array('asdfasdfadsf.com', \Piwik\Piwik::translate('General_Unknown')), + array('http://xwayn.com', \Piwik\Piwik::translate('General_Unknown')), + array('http://live.com/test', \Piwik\Piwik::translate('General_Unknown')), + ); + } + + /** + * @dataProvider getSocialNetworkFromDomainTestData + */ + public function testGetSocialNetworkFromDomain($url, $expected) + { + $this->assertEquals($expected, Social::getInstance()->getSocialNetworkFromDomain($url)); + } + + public function getLogoFromUrlTestData() + { + return array( + array('http://www.facebook.com', 'facebook.com.png'), + array('www.facebook.com', 'facebook.com.png',), + array('http://lastfm.com.tr', 'last.fm.png'), + array('http://asdfasdf.org/test', 'xx.png'), + array('http://www.google.com', 'xx.png'), + ); + } + + /** + * @group Plugins + * + * @dataProvider getLogoFromUrlTestData + */ + public function testGetLogoFromUrl($url, $expected) + { + $this->assertContains($expected, Social::getInstance()->getLogoFromUrl($url)); + } +}
\ No newline at end of file diff --git a/plugins/Resolution/lang/ko.json b/plugins/Resolution/lang/ko.json index d76113b7af..19be5c511d 100644 --- a/plugins/Resolution/lang/ko.json +++ b/plugins/Resolution/lang/ko.json @@ -3,6 +3,7 @@ "ColumnConfiguration": "구성", "ColumnResolution": "해상도", "Configurations": "구성", + "PluginDescription": "방문자의 화면 해상도를 기록\/보고합니다.", "Resolutions": "해상도", "WidgetGlobalVisitors": "글로벌 방문자 구성", "WidgetGlobalVisitorsDocumentation": "방문자의 가장 일반적인 사용 환경에 대한 보고서입니다. 운영 체제, 브라우저 종류와 화면 해상도의 조합으로 표시합니다.", diff --git a/plugins/SEO/Metric/Alexa.php b/plugins/SEO/Metric/Alexa.php index 87c5785d36..e8f7956241 100644 --- a/plugins/SEO/Metric/Alexa.php +++ b/plugins/SEO/Metric/Alexa.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -42,7 +43,7 @@ class Alexa implements MetricsProvider $value = null; } - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://alexa.com'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://alexa.com'); $link = self::LINK . urlencode($domain); return array( diff --git a/plugins/SEO/Metric/Bing.php b/plugins/SEO/Metric/Bing.php index e85d585c13..71553ffc6e 100644 --- a/plugins/SEO/Metric/Bing.php +++ b/plugins/SEO/Metric/Bing.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -46,7 +47,7 @@ class Bing implements MetricsProvider $pageCount = null; } - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://bing.com'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://bing.com'); return array( new Metric('bing-index', 'SEO_Bing_IndexedPages', $pageCount, $logo, null, null, 'General_Pages') diff --git a/plugins/SEO/Metric/Dmoz.php b/plugins/SEO/Metric/Dmoz.php index d21be1e5ca..c7b8860a89 100644 --- a/plugins/SEO/Metric/Dmoz.php +++ b/plugins/SEO/Metric/Dmoz.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -53,7 +54,7 @@ class Dmoz implements MetricsProvider $value = null; } - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://dmoz.org'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://dmoz.org'); return array( new Metric('dmoz', 'SEO_Dmoz', $value, $logo) diff --git a/plugins/SEO/Metric/Google.php b/plugins/SEO/Metric/Google.php index cec1d96af9..7abd82b371 100644 --- a/plugins/SEO/Metric/Google.php +++ b/plugins/SEO/Metric/Google.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SEO\Metric; use Piwik\Http; use Piwik\NumberFormatter; +use Piwik\Plugins\Referrers\SearchEngine; use Psr\Log\LoggerInterface; /** @@ -37,7 +38,7 @@ class Google implements MetricsProvider $pageCount = $this->fetchIndexedPagesCount($domain); $pageRank = $this->fetchPageRank($domain); - $logo = \Piwik\Plugins\Referrers\getSearchEngineLogoFromUrl('http://google.com'); + $logo = SearchEngine::getInstance()->getLogoFromUrl('http://google.com'); return array( new Metric('google-index', 'SEO_Google_IndexedPages', $pageCount, $logo, null, null, 'General_Pages'), diff --git a/plugins/SEO/lang/ja.json b/plugins/SEO/lang/ja.json index e0496470df..e4459709a2 100644 --- a/plugins/SEO/lang/ja.json +++ b/plugins/SEO/lang/ja.json @@ -1,5 +1,6 @@ { "SEO": { + "PluginDescription": "このプラグインは SEO メトリクスを抽出し、表示します。: Alexa web ランキング, Google ページランク,インデックスされたページ数やバックリンクの数。", "AlexaRank": "Alexa ランク", "Bing_IndexedPages": "Bing インデックスページ", "Dmoz": "DMOZ エントリー", diff --git a/plugins/SEO/lang/ko.json b/plugins/SEO/lang/ko.json index 55f71074cb..de4e2b2bd6 100644 --- a/plugins/SEO/lang/ko.json +++ b/plugins/SEO/lang/ko.json @@ -1,5 +1,6 @@ { "SEO": { + "PluginDescription": "이 플러그인은 Alexa 웹 랭킹, 구글 페이지랭크, 색인된 페이지 수, 선택된 웹사이트의 백링크와 같은 SEO 측정 기준을 추출하고 보여줍니다.", "AlexaRank": "Alexa 랭크", "Bing_IndexedPages": "Bing에 색인된 페이지", "Dmoz": "DMOZ 항목", diff --git a/plugins/ScheduledReports/config/tcpdf_config.php b/plugins/ScheduledReports/config/tcpdf_config.php index 0fd19a1232..26b33b7da7 100644 --- a/plugins/ScheduledReports/config/tcpdf_config.php +++ b/plugins/ScheduledReports/config/tcpdf_config.php @@ -13,7 +13,7 @@ use Piwik\Container\StaticContainer; * */ -define('K_PATH_MAIN', PIWIK_VENDOR_PATH . '/tecnick.com/tcpdf/'); +define('K_PATH_MAIN', PIWIK_VENDOR_PATH . '/tecnickcom/tcpdf/'); $pathTmpTCPDF = StaticContainer::get('path.tmp') . '/tcpdf/'; diff --git a/plugins/ScheduledReports/lang/cs.json b/plugins/ScheduledReports/lang/cs.json index 836eb47133..caa1cb992e 100644 --- a/plugins/ScheduledReports/lang/cs.json +++ b/plugins/ScheduledReports/lang/cs.json @@ -4,7 +4,7 @@ "AggregateReportsFormat_GraphsOnly": "Pouze zobrazit grafy (ne tabulky hlášení)", "AggregateReportsFormat_TablesAndGraphs": "Zobrazit tabulky hlášení a grafy pro všechna hlášení", "AggregateReportsFormat_TablesOnly": "(Výchozí) zobrazit tabulky hlášení (grafy pouze pro klíčová měření)", - "AlsoSendReportToTheseEmails": "Odesílat hlášení také na tyto e-maily (jeden e-mail na řádek):", + "AlsoSendReportToTheseEmails": "Odesílat hlášení také na tyto emaily (jeden email na řádek):", "AreYouSureDeleteReport": "Opravdu chcete odstranit toto hlášení a jeho plán?", "CancelAndReturnToReports": "Zrušit a %svrátit se k seznamu hlášení%s", "CreateAndScheduleReport": "Vytvořit a naplánovat hlášení", @@ -13,7 +13,7 @@ "DescriptionOnFirstPage": "Popis hlášení bude zobrazen na první jeho straně.", "DisplayFormat_TablesOnly": "Pouze zobrazit tabulky (žádné grafy)", "EmailHello": "Ahoj,", - "EmailReports": "Poslat hlášení na e-mail", + "EmailReports": "Poslat hlášení na email", "EmailSchedule": "Plánování zasílání hlášení", "EvolutionGraph": "Zobrazit historické grafy pro %s nejvyšších hodnot", "FrontPage": "Hlavní strana", @@ -34,15 +34,15 @@ "ReportsIncluded": "Zahrnuté statistiky", "ReportType": "Odeslat hlášení pomocí", "ReportUpdated": "Hlášení aktualizováno", - "Segment_Deletion_Error": "Tento segment nemůže být smazán nebo zneviditelněn, protože se používá k vytváření e-mailových hlášení %s. Zkuste to znovu poté, co ho z těchto hlášení odstraníte.", - "Segment_Help": "Můžete zvolit existující segment, který bude aplikován na data v tomto e-mailovém hlášení. Segmenty můžete vytvářet a upravovat vlastní segmenty na vaší nástěnce %s(klikněte zde pro otevření)%s, pak klikněte na box \"%s\", pak \"%s\".", + "Segment_Deletion_Error": "Tento segment nemůže být smazán nebo zneviditelněn, protože se používá k vytváření emailových hlášení %s. Zkuste to znovu poté, co ho z těchto hlášení odstraníte.", + "Segment_Help": "Můžete zvolit existující segment, který bude aplikován na data v tomto emailovém hlášení. Segmenty můžete vytvářet a upravovat vlastní segmenty na vaší nástěnce %s(klikněte zde pro otevření)%s, pak klikněte na box \"%s\", pak \"%s\".", "SegmentAppliedToReports": "Segment %s je aplikován na hlášení.", "SendReportNow": "Odeslat hlášení ihned", "SendReportTo": "Odeslat hlášení", "SentToMe": "Odeslat hlášení mně", "TableOfContent": "Seznam hlášení", "ThereIsNoReportToManage": "Pro stránky %s neexistuje žádné hlášení, které lze spravovat", - "TopLinkTooltip": "Vytvářejte vlastní hlášení, která budou automaticky doručena na vaši nebo zákazníkovu e-mailovou adresu.", + "TopLinkTooltip": "Vytvářejte vlastní hlášení, která budou automaticky doručena na vaši nebo zákazníkovu emailovou adresu.", "TopOfReport": "Zpět nahoru", "UpdateReport": "Aktualizovat hlášení", "WeeklyScheduleHelp": "Týdenní plán: hlášení bude odesláno každé pondělí" diff --git a/plugins/ScheduledReports/lang/it.json b/plugins/ScheduledReports/lang/it.json index 5f9e17ee17..bf796fd1c3 100644 --- a/plugins/ScheduledReports/lang/it.json +++ b/plugins/ScheduledReports/lang/it.json @@ -25,7 +25,7 @@ "PiwikReports": "Report di Piwik", "PleaseFindAttachedFile": "Potete trovare nel file allegato il tuo %1$s report per %2$s.", "SentFromX": "Inviato da %s", - "PleaseFindBelow": "Di seguito trovi il tuo %1$s report per %2$s.", + "PleaseFindBelow": "Di seguito trovi il tuo report %1$s per %2$s.", "PluginDescription": "Crea dei report personalizzati e li programma per l'invio tramite email ogni giorno, settimana o mese a una o più persone. Sono supportati svariati formati (html, pdf, csv, immagini).", "ReportFormat": "Formato report", "ReportHour": "Invia report alle %s", diff --git a/plugins/ScheduledReports/lang/ja.json b/plugins/ScheduledReports/lang/ja.json index 15e24efd81..677a778017 100644 --- a/plugins/ScheduledReports/lang/ja.json +++ b/plugins/ScheduledReports/lang/ja.json @@ -24,9 +24,11 @@ "Pagination": "%s の %s ページ", "PiwikReports": "Piwik リポート", "PleaseFindAttachedFile": "%2$s の %1$s のリポートを添付しています。", + "SentFromX": "%s から送信されます。", "PleaseFindBelow": "%2$s の %1$s のリポートをお届けします。", "PluginDescription": "カスタム レポートを作成し、毎日、毎週、毎月等、 1 つまたは複数の人にメールで送信するようにスケジュールします。サポートされているレポート形式 ( html、pdf、csv、画像)。", "ReportFormat": "リポートのフォーマット", + "ReportHour": "%s 時にレポートを送信します", "ReportIncludeNWebsites": "このレポートは、現在利用可能な %s ウェブサイトから、少なくとも 1 訪問以上ある全ウェブサイトの主なメトリクスを含みます。", "ReportSent": "送信されたレポート", "ReportsIncluded": "リポートに含める情報", diff --git a/plugins/ScheduledReports/lang/tr.json b/plugins/ScheduledReports/lang/tr.json index 818cb4378b..04e359c5a4 100644 --- a/plugins/ScheduledReports/lang/tr.json +++ b/plugins/ScheduledReports/lang/tr.json @@ -1,8 +1,12 @@ { "ScheduledReports": { + "AggregateReportsFormat": "Ekran seçenekleri (isteğe bağlı)", "CreateReport": "Rapor Oluştur", "EmailHello": "Merhaba,", "EmailReports": "Raporları epostala", + "ReportFormat": "Rapor Formatı", + "ReportUpdated": "Rapor güncellendi", + "SendReportNow": "Raporu şimdi gönder", "TableOfContent": "Rapor listesi", "TopOfReport": "Başa dön", "UpdateReport": "Raporu Güncelle" diff --git a/plugins/SecurityInfo b/plugins/SecurityInfo -Subproject 65c7b5a1693b99dc97b78df015df1f606b2ef8b +Subproject ffbc301bf148481b7896f685847ada3bc9734b5 diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php index 9107935e73..a46be068bc 100644 --- a/plugins/SegmentEditor/SegmentEditor.php +++ b/plugins/SegmentEditor/SegmentEditor.php @@ -28,6 +28,7 @@ class SegmentEditor extends \Piwik\Plugin 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'Template.nextToCalendar' => 'getSegmentEditorHtml', + 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', ); } @@ -85,4 +86,9 @@ class SegmentEditor extends \Piwik\Plugin { return Config::getInstance()->General['allow_adding_segments_for_all_websites'] == 1; } + + public function getClientSideTranslationKeys(&$translationKeys) + { + $translationKeys[] = 'SegmentEditor_CustomSegment'; + } } diff --git a/plugins/SegmentEditor/SegmentFormatter.php b/plugins/SegmentEditor/SegmentFormatter.php new file mode 100644 index 0000000000..19838d46d5 --- /dev/null +++ b/plugins/SegmentEditor/SegmentFormatter.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\SegmentEditor; + +use Exception; +use Piwik\Config; +use Piwik\Db; +use Piwik\Piwik; +use Piwik\Segment; +use Piwik\Segment\SegmentExpression; + +/** + */ +class SegmentFormatter +{ + /** + * @var SegmentList + */ + private $segmentList; + + private $matchesMetric = array( + SegmentExpression::MATCH_EQUAL => 'General_OperationEquals', + SegmentExpression::MATCH_NOT_EQUAL => 'General_OperationNotEquals', + SegmentExpression::MATCH_LESS_OR_EQUAL => 'General_OperationAtMost', + SegmentExpression::MATCH_GREATER_OR_EQUAL => 'General_OperationAtLeast', + SegmentExpression::MATCH_LESS => 'General_OperationLessThan', + SegmentExpression::MATCH_GREATER => 'General_OperationGreaterThan', + ); + + private $matchesDimension = array( + SegmentExpression::MATCH_EQUAL => 'General_OperationIs', + SegmentExpression::MATCH_NOT_EQUAL => 'General_OperationIsNot', + SegmentExpression::MATCH_CONTAINS => 'General_OperationContains', + SegmentExpression::MATCH_DOES_NOT_CONTAIN => 'General_OperationDoesNotContain', + SegmentExpression::MATCH_STARTS_WITH => 'General_OperationStartsWith', + SegmentExpression::MATCH_ENDS_WITH => 'General_OperationEndsWith' + ); + + private $operators = array( + SegmentExpression::BOOL_OPERATOR_AND => 'General_And', + SegmentExpression::BOOL_OPERATOR_OR => 'General_Or', + SegmentExpression::BOOL_OPERATOR_END => '', + ); + + public function __construct(SegmentList $segmentList) + { + $this->segmentList = $segmentList; + } + + public function getHumanReadable($segmentString, $idSite) + { + if (empty($segmentString)) { + return Piwik::translate('SegmentEditor_DefaultAllVisits'); + } + + try { + $segment = new SegmentExpression(urldecode($segmentString)); + $expressions = $segment->parseSubExpressions(); + } catch (Exception $e) { + $segment = new SegmentExpression($segmentString); + $expressions = $segment->parseSubExpressions(); + } + + $readable = ''; + foreach ($expressions as $expression) { + $operator = $expression[SegmentExpression::INDEX_BOOL_OPERATOR]; + $operand = $expression[SegmentExpression::INDEX_OPERAND]; + $name = $operand[SegmentExpression::INDEX_OPERAND_NAME]; + + $segment = $this->segmentList->findSegment($name, $idSite); + + if (empty($segment)) { + throw new Exception(sprintf("The segment '%s' does not exist.", $name)); + } + + $readable .= $segment['name'] . ' '; + $readable .= $this->getTranslationForComparison($operand, $segment['type']) . ' '; + $readable .= $this->getFormattedValue($operand); + $readable .= $this->getTranslationForBoolOperator($operator) . ' '; + } + + $readable = trim($readable); + + return $readable; + } + + private function getTranslationForComparison($operand, $segmentType) + { + $operator = $operand[SegmentExpression::INDEX_OPERAND_OPERATOR]; + + $translation = $operator; + + if ($operator === SegmentExpression::MATCH_IS_NULL_OR_EMPTY) { + return Piwik::translate('SegmentEditor_SegmentOperatorIsNullOrEmpty'); + } + + if ($operator === SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY) { + return Piwik::translate('SegmentEditor_SegmentOperatorIsNotNullNorEmpty'); + } + + if ($segmentType === 'dimension' && !empty($this->matchesDimension[$operator])) { + $translation = Piwik::translate($this->matchesDimension[$operator]); + } + if ($segmentType === 'metric' && !empty($this->matchesMetric[$operator])) { + $translation = Piwik::translate($this->matchesMetric[$operator]); + } + + return strtolower($translation); + } + + private function getFormattedValue($operand) + { + $operator = $operand[SegmentExpression::INDEX_OPERAND_OPERATOR]; + + if ($operator === SegmentExpression::MATCH_IS_NULL_OR_EMPTY + || $operator === SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY) { + return ''; + } + + $value = $operand[SegmentExpression::INDEX_OPERAND_VALUE]; + + if (empty($value)) { + $value = ''; + } + + return '"' . $value . '" '; + } + + private function getTranslationForBoolOperator($operator) + { + $translation = ''; + + if (!empty($this->operators[$operator])) { + $translation = Piwik::translate($this->operators[$operator]); + } elseif (!empty($operator)) { + $translation = $operator; + } + + return $translation; + } +} diff --git a/plugins/SegmentEditor/SegmentList.php b/plugins/SegmentEditor/SegmentList.php new file mode 100644 index 0000000000..e7094d4793 --- /dev/null +++ b/plugins/SegmentEditor/SegmentList.php @@ -0,0 +1,32 @@ +<?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\SegmentEditor; + +use Piwik\API\Request; +use Piwik\Config; +use Piwik\Db; + +/** + */ +class SegmentList +{ + public function findSegment($segmentName, $idSite) + { + $segments = Request::processRequest('API.getSegmentsMetadata', array( + 'idSites' => array($idSite), + )); + + foreach ($segments as $segment) { + if ($segment['segment'] == $segmentName && !empty($segmentName)) { + return $segment; + } + } + } + +} diff --git a/plugins/SegmentEditor/SegmentSelectorControl.php b/plugins/SegmentEditor/SegmentSelectorControl.php index e37354e1cd..72be071e5e 100644 --- a/plugins/SegmentEditor/SegmentSelectorControl.php +++ b/plugins/SegmentEditor/SegmentSelectorControl.php @@ -8,8 +8,10 @@ */ namespace Piwik\Plugins\SegmentEditor; +use Piwik\API\Request; use Piwik\Common; use Piwik\Config; +use Piwik\Container\StaticContainer; use Piwik\Piwik; use Piwik\Plugins\API\API as APIMetadata; use Piwik\View\UIControl; @@ -37,13 +39,17 @@ class SegmentSelectorControl extends UIControl $this->selectedSegment = Common::getRequestVar('segment', false, 'string'); + $formatter = StaticContainer::get('Piwik\Plugins\SegmentEditor\SegmentFormatter'); + $this->segmentDescription = $formatter->getHumanReadable(Request::getRawSegmentFromRequest(), $this->idSite); + $this->isAddingSegmentsForAllWebsitesEnabled = SegmentEditor::isAddingSegmentsForAllWebsitesEnabled(); $segments = APIMetadata::getInstance()->getSegmentsMetadata($this->idSite); + $visitTitle = Piwik::translate('General_Visit'); $segmentsByCategory = array(); foreach ($segments as $segment) { - if ($segment['category'] == Piwik::translate('General_Visit') + if ($segment['category'] == $visitTitle && ($segment['type'] == 'metric' && $segment['segment'] != 'visitIp') ) { $metricsLabel = Piwik::translate('General_Metrics'); @@ -52,7 +58,6 @@ class SegmentSelectorControl extends UIControl } $segmentsByCategory[$segment['category']][] = $segment; } - uksort($segmentsByCategory, array($this, 'sortSegmentCategories')); $this->createRealTimeSegmentsIsEnabled = Config::getInstance()->General['enable_create_realtime_segments']; $this->segmentsByCategory = $segmentsByCategory; @@ -95,15 +100,6 @@ class SegmentSelectorControl extends UIControl return (bool) $savedSegment['auto_archive']; } - public function sortSegmentCategories($a, $b) - { - // Custom Variables last - if ($a == Piwik::translate('CustomVariables_CustomVariables')) { - return 1; - } - return 0; - } - private function getTranslations() { $translationKeys = array( @@ -115,6 +111,8 @@ class SegmentSelectorControl extends UIControl 'General_OperationGreaterThan', 'General_OperationContains', 'General_OperationDoesNotContain', + 'General_OperationStartsWith', + 'General_OperationEndsWith', 'General_OperationIs', 'General_OperationIsNot', 'General_OperationContains', diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index 0e47bd7d19..2ffd7dac57 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -49,6 +49,8 @@ Segmentation = (function($) { self.availableMatches["dimension"]["!="] = self.translations['General_OperationIsNot']; self.availableMatches["dimension"]["=@"] = self.translations['General_OperationContains']; self.availableMatches["dimension"]["!@"] = self.translations['General_OperationDoesNotContain']; + self.availableMatches["dimension"]["=^"] = self.translations['General_OperationStartsWith']; + self.availableMatches["dimension"]["=$"] = self.translations['General_OperationEndsWith']; segmentation.prototype.setAvailableSegments = function (segments) { this.availableSegments = segments; @@ -83,7 +85,7 @@ Segmentation = (function($) { var name = $(foundItems).first().find("span.segname").text(); title.text(name); } else { - title.text("Custom Segment"); + title.text(_pk_translate('SegmentEditor_CustomSegment')); } segmentationTitle.html(title); } @@ -264,7 +266,7 @@ Segmentation = (function($) { }; var findAndExplodeByMatch = function(metric){ - var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">"]; + var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">", "=^", "=$"]; var newMetric = {}; var minPos = metric.length; var match, index; diff --git a/plugins/SegmentEditor/lang/cs.json b/plugins/SegmentEditor/lang/cs.json index 36dd2084b3..c5752de9e2 100644 --- a/plugins/SegmentEditor/lang/cs.json +++ b/plugins/SegmentEditor/lang/cs.json @@ -7,13 +7,14 @@ "AutoArchivePreProcessed": "Segmentovaná hlášení jsou předzpracována rychleji (vyžadují cron)", "AutoArchiveRealTime": "Segmentovaná hlášení jsou zpracována v reálném čase", "ChooseASegment": "Zvolte segment", + "CurrentlySelectedSegment": "Aktuálně vybraný segment: %s", "DataAvailableAtLaterDate": "Vaše segmentovaná analytická hlášení budou dostupná později. Omlouváme se za tuto nepříjemnost.", "DefaultAllVisits": "Všechny návštěvy", "DragDropCondition": "Přetáhněte podmínku", "LoadingSegmentedDataMayTakeSomeTime": "Zpracování segmentovaných dat návštěvníků může pár minut trvat...", "OperatorAND": "AND", "OperatorOR": "OR", - "SaveAndApply": "Uložit & použít", + "SaveAndApply": "Uložit a použít", "SegmentDisplayedAllWebsites": "Všechny webové stránky", "SegmentDisplayedThisWebsiteOnly": "Pouze tyto webové stránky", "SegmentIsDisplayedForWebsite": "A zobrazený po", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "Jinak můžete nastavení změnit v souboru %s, nebo můžete upravit tento segment a zvolit %s.", "YouMustBeLoggedInToCreateSegments": "Pro vytváření a úpravu vlastních segmentů návštěvníků musíte být přihlášen.", "YouDontHaveAccessToCreateSegments": "Pro vytváření a úpravu segmentů nemáte požadovanou přístupovou úroveň.", - "AddingSegmentForAllWebsitesDisabled": "Přidávání segmentů pro všechny stránky bylo zakázáno." + "AddingSegmentForAllWebsitesDisabled": "Přidávání segmentů pro všechny stránky bylo zakázáno.", + "SegmentXIsAUnionOf": "%s je propojení těchto segmentů:", + "CustomSegment": "Vlastní segment", + "SegmentOperatorIsNullOrEmpty": "je nulový nebo prázdný", + "SegmentOperatorIsNotNullNorEmpty": "není nulový nebo prázdný" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/el.json b/plugins/SegmentEditor/lang/el.json index e0a9f14a6a..72d3659543 100644 --- a/plugins/SegmentEditor/lang/el.json +++ b/plugins/SegmentEditor/lang/el.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "οι αναφορές τμημάτων προεπεξεργάζονται (γρηγορότερα, απαιτείται το archive.php με cron)", "AutoArchiveRealTime": "οι αναφορές τμημάτων επεξεργάζονται σε πραγματικό χρόνο", "ChooseASegment": "Επιλέξτε ένα τμήμα", + "CurrentlySelectedSegment": "Επιλεγμένο τμήμα αυτή τη στιγμή: %s", "DataAvailableAtLaterDate": "Οι τμηματικές αναφορές αναλυτικών θα είναι αργότερα διαθέσιμες. Ζητούμε συγγνώμη για την ταλαιπωρία.", "DefaultAllVisits": "Όλες οι επισκέψεις", "DragDropCondition": "Συνθήκη με Σύρε & Άσε", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "Εναλλακτικά, μπορείτε να αλλάξετε τη ρύθμιση στο αρχείο ρυθμίσεων (%s), ή να τροποποιήσετε το Τμήμα αυτό και να επιλέξετε '%s'.", "YouMustBeLoggedInToCreateSegments": "Θα πρέπει να έχετε κάνει είσοδο για να δημιουργήσετε και να επεξεργαστείτε προσαρμοσμένα τμήματα επισκεπτών.", "YouDontHaveAccessToCreateSegments": "Δεν διαθέτετε την απαιτούμενη πρόσβαση ασφαλείας για να δημιουργείτε και να τροποποιείτε τμήματα.", - "AddingSegmentForAllWebsitesDisabled": "Η προσθήκη τμημάτων έχει απενεργοποιηθεί για όλους τους ιστοτόπους." + "AddingSegmentForAllWebsitesDisabled": "Η προσθήκη τμημάτων έχει απενεργοποιηθεί για όλους τους ιστοτόπους.", + "SegmentXIsAUnionOf": "Το %s είναι η ένωση αυτών των τμημάτων:", + "CustomSegment": "Προσαρμοσμένο τμήμα", + "SegmentOperatorIsNullOrEmpty": "είναι άκυρο (null) ή κενό", + "SegmentOperatorIsNotNullNorEmpty": "δεν είναι άκυρο (null) ή άδειο" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/en.json b/plugins/SegmentEditor/lang/en.json index 46bdf5fef2..8651afeecd 100644 --- a/plugins/SegmentEditor/lang/en.json +++ b/plugins/SegmentEditor/lang/en.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "segmented reports are pre-processed (faster, requires cron)", "AutoArchiveRealTime": "segmented reports are processed in real time", "ChooseASegment": "Choose a segment", + "CurrentlySelectedSegment": "Currently selected segment: %s", "DataAvailableAtLaterDate": "Your segmented analytics reports will be available later. We apologize for the inconvenience.", "DefaultAllVisits": "All visits", "DragDropCondition": "Drag & Drop condition", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "Alternatively you may change the setting in the config file (%s), or edit this Segment and choose '%s'.", "YouMustBeLoggedInToCreateSegments": "You must be logged in to create and edit custom visitor segments.", "YouDontHaveAccessToCreateSegments": "You don't have the required access level to create and edit segments.", - "AddingSegmentForAllWebsitesDisabled": "Adding segments for all websites has been disabled." + "AddingSegmentForAllWebsitesDisabled": "Adding segments for all websites has been disabled.", + "SegmentXIsAUnionOf": "%s is a union of these segments:", + "CustomSegment": "Custom Segment", + "SegmentOperatorIsNullOrEmpty": "is null or empty", + "SegmentOperatorIsNotNullNorEmpty": "is not null nor empty" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/it.json b/plugins/SegmentEditor/lang/it.json index 63b0ec0ce2..28b4b9f3ed 100644 --- a/plugins/SegmentEditor/lang/it.json +++ b/plugins/SegmentEditor/lang/it.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "i reports segmentati sono pre-elaborati (più veloce, richiede un cron-job)", "AutoArchiveRealTime": "i reports segmentati sono elaborati in tempo reale", "ChooseASegment": "Scegli un segmento", + "CurrentlySelectedSegment": "Segmento attualmente selezionato: %s", "DataAvailableAtLaterDate": "I report segmentati delle statistiche saranno disponibili più tardi. Ci scusiamo per l'inconveniente.", "DefaultAllVisits": "Tutte le visite", "DragDropCondition": "Copia & Incolla condizione", @@ -26,6 +27,10 @@ "YouMayChangeSetting": "In alternativa, puoi cambiare le impostazioni nel file di configurazione (%s) o modificare questo Segmento e scegliere '%s'.", "YouMustBeLoggedInToCreateSegments": "Devi avere effettuato l'accesso per creare e modificare i segmenti personalizzati dei visitatori.", "YouDontHaveAccessToCreateSegments": "Non hai un livello d'accesso adeguato per creare e modificare i segmenti.", - "AddingSegmentForAllWebsitesDisabled": "L'aggiunta di segmenti per tutti i siti è stata disabilitata." + "AddingSegmentForAllWebsitesDisabled": "L'aggiunta di segmenti per tutti i siti è stata disabilitata.", + "SegmentXIsAUnionOf": "%s è un'unione di questi segmenti:", + "CustomSegment": "Segmento Speciale", + "SegmentOperatorIsNullOrEmpty": "è nullo o vuoto", + "SegmentOperatorIsNotNullNorEmpty": "non è nullo nè vuoto" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/ja.json b/plugins/SegmentEditor/lang/ja.json index 8c65a9c7cf..0156de56ab 100644 --- a/plugins/SegmentEditor/lang/ja.json +++ b/plugins/SegmentEditor/lang/ja.json @@ -1,5 +1,6 @@ { "SegmentEditor": { + "PluginDescription": "セグメントエディターでカスタム ユーザー セグメントを作成、編集できます。", "AddANDorORCondition": "%s の条件を追加", "AddNewSegment": "新しいセグメントを追加", "AreYouSureDeleteSegment": "このセグメントを削除してもよろしいですか?", @@ -25,6 +26,7 @@ "YouMayChangeSetting": "別の方法としては設定ファイル (%s) で設定を変更するか、このセグメント '%s' を選択し編集することができます。", "YouMustBeLoggedInToCreateSegments": "ビジターのカスタムセグメントの作成と編集にはログインが必要です。", "YouDontHaveAccessToCreateSegments": "セグメントの作成および編集に必要なレベルのアクセス権限を持っていません。", - "AddingSegmentForAllWebsitesDisabled": "全ウェブサイトに対するセグメントは追加できませんでした。" + "AddingSegmentForAllWebsitesDisabled": "全ウェブサイトに対するセグメントは追加できませんでした。", + "CustomSegment": "カスタムセグメント" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/pt-br.json b/plugins/SegmentEditor/lang/pt-br.json index a5f8df59f2..eb7a8919d2 100644 --- a/plugins/SegmentEditor/lang/pt-br.json +++ b/plugins/SegmentEditor/lang/pt-br.json @@ -7,6 +7,7 @@ "AutoArchivePreProcessed": "Relatórios segmentados são pré-processados (Para agilizar, é necessário usar o cron em: archive.php)", "AutoArchiveRealTime": "Relatórios segmentados são processados em tempo real", "ChooseASegment": "Escolha um segmento", + "CurrentlySelectedSegment": "Segmento atualmente selecionado: %s", "DataAvailableAtLaterDate": "Os seus relatórios de análise segmentados estarão disponíveis mais tarde. Pedimos desculpas pela inconveniência.", "DefaultAllVisits": "Todas as visitas", "DragDropCondition": "Condição Drag & Drop", @@ -23,8 +24,13 @@ "ThisSegmentIsVisibleTo": "Este segmento é visível para:", "VisibleToAllUsers": "Todos os Usuários", "VisibleToMe": "mim", + "YouMayChangeSetting": "Alternativamente, você pode alterar a configuração no arquivo de configuração (%s), ou editar este Segmento e escolher %s.", "YouMustBeLoggedInToCreateSegments": "Você precisa estar logado para criar e editar segmentos personalizados de visitantes.", "YouDontHaveAccessToCreateSegments": "Você não tem o nível de acesso necessário para criar e editar segmentos.", - "AddingSegmentForAllWebsitesDisabled": "Adicionar segmentos para todos os sites foi desativado." + "AddingSegmentForAllWebsitesDisabled": "Adicionar segmentos para todos os sites foi desativado.", + "SegmentXIsAUnionOf": "%s é uma união destes segmentos:", + "CustomSegment": "Segmento Personalizado", + "SegmentOperatorIsNullOrEmpty": "está nulo ou vazio", + "SegmentOperatorIsNotNullNorEmpty": "não está nulo nem vazio" } }
\ No newline at end of file diff --git a/plugins/SegmentEditor/lang/tr.json b/plugins/SegmentEditor/lang/tr.json new file mode 100644 index 0000000000..3541941a61 --- /dev/null +++ b/plugins/SegmentEditor/lang/tr.json @@ -0,0 +1,8 @@ +{ + "SegmentEditor": { + "OperatorAND": "VE", + "OperatorOR": "VEYA", + "SaveAndApply": "Kaydet & Uygula", + "VisibleToAllUsers": "tüm kullanıcılar" + } +}
\ No newline at end of file diff --git a/plugins/SegmentEditor/templates/_segmentSelector.twig b/plugins/SegmentEditor/templates/_segmentSelector.twig index 7a8019d37e..dc269dc8b9 100644 --- a/plugins/SegmentEditor/templates/_segmentSelector.twig +++ b/plugins/SegmentEditor/templates/_segmentSelector.twig @@ -1,5 +1,5 @@ <div class="SegmentEditor" style="display:none;"> - <div class="segmentationContainer listHtml" title="{{ 'SegmentEditor_ChooseASegment'|translate|e('html_attr') }}"> + <div class="segmentationContainer listHtml" title="{{ 'SegmentEditor_ChooseASegment'|translate|e('html_attr') }}. {{ 'SegmentEditor_CurrentlySelectedSegment'|translate(segmentDescription)|e('html_attr') }}"> <a class="title"><span class="icon icon-segment"></span><span class="segmentationTitle"></span></a> <div class="dropdown dropdown-body"> <div class="segmentFilterContainer"> @@ -61,6 +61,8 @@ <option value=">">{{ 'General_OperationGreaterThan'|translate }}</option> <option value="=@">{{ 'General_OperationContains'|translate }}</option> <option value="!@">{{ 'General_OperationDoesNotContain'|translate }}</option> + <option value="=^">{{ 'General_OperationStartsWith'|translate }}</option> + <option value="=$">{{ 'General_OperationEndsWith'|translate }}</option> </select> </div> <div class="segment-input metricValueBlock"> @@ -99,7 +101,17 @@ <a class="metric_category" href="#">{{ category }}</a> <ul style="display:none;"> {% for segmentInCategory in segmentsInCategory %} - <li data-metric="{{ segmentInCategory.segment }}"><a class="ddmetric" href="#">{{ segmentInCategory.name }}</a></li> + {% set title = segmentInCategory.name %} + {% if segmentInCategory.unionOfSegments is defined and segmentInCategory.unionOfSegments %} + {% set title = 'SegmentEditor_SegmentXIsAUnionOf'|translate(title) %} + {% for unionSegment in segmentInCategory.unionOfSegments %} + {% set title = title ~ ' ' ~ unionSegment %} + {% if not loop.last %} + {% set title = title ~ ',' %} + {% endif %} + {% endfor %} + {% endif %} + <li data-metric="{{ segmentInCategory.segment }}" title="{{ title|e('html_attr') }}"><a class="ddmetric" href="#">{{ segmentInCategory.name }}</a></li> {% endfor %} </ul> </li> diff --git a/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php b/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php new file mode 100644 index 0000000000..ca37e0775b --- /dev/null +++ b/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php @@ -0,0 +1,116 @@ +<?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\SegmentEditor\tests\Integration; + +use Piwik\Plugins\SegmentEditor\SegmentFormatter; +use Piwik\Plugins\SegmentEditor\SegmentList; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Translate; +use Exception; + +/** + * @group SegmentFormatterTest + * @group SegmentFormatter + * @group SegmentEditor + * @group Plugins + */ +class SegmentFormatterTest extends IntegrationTestCase +{ + /** + * @var SegmentFormatter + */ + private $formatter; + + private $idSite; + + public function setUp() + { + parent::setUp(); + + $this->idSite = Fixture::createWebsite('2012-01-01 00:00:00'); + $this->formatter = new SegmentFormatter(new SegmentList()); + + Translate::loadAllTranslations(); + } + + public function tearDown() + { + Translate::reset(); + } + + public function test_getHumanReadable_noSegmentGiven_ShouldReturnDefaultSegment() + { + $readable = $this->formatter->getHumanReadable($segment = '', $this->idSite); + $this->assertSame('All visits', $readable); + } + + public function test_getHumanReadable_ShouldTranslateAMetric() + { + $readable = $this->formatter->getHumanReadable($segment = 'visitCount>5', $this->idSite); + $this->assertSame('Number of visits greater than "5"', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'visitCount==5', $this->idSite); + $this->assertSame('Number of visits equals "5"', $readable); + } + + public function test_getHumanReadable_ShouldTranslateADimension() + { + $readable = $this->formatter->getHumanReadable($segment = 'resolution=@1024', $this->idSite); + $this->assertSame('Resolution contains "1024"', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'resolution==1024x768', $this->idSite); + $this->assertSame('Resolution is "1024x768"', $readable); + } + + public function test_getHumanReadable_ShouldCombineMultipleSegmentDefinitionsWithBooleanOperator() + { + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=1.0;browserEngine=$Trident', $this->idSite); + $this->assertSame('Browser version is not "1.0" and Browser engine ends with "Trident"', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=1.0,browserEngine=$Trident', $this->idSite); + $this->assertSame('Browser version is not "1.0" or Browser engine ends with "Trident"', $readable); + } + + public function test_getHumanReadable_ShouldHandleAMissingValue() + { + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion==', $this->idSite); + $this->assertSame('Browser version is null or empty', $readable); + + $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=', $this->idSite); + $this->assertSame('Browser version is not null nor empty', $readable); + } + + public function test_getHumanReadable_ShouldHandleAUrlDecodedSegment() + { + $readable = $this->formatter->getHumanReadable($segment = 'pageUrl%3D%40piwik%2CvisitId!%3D1', $this->idSite); + $this->assertSame('Page URL contains "piwik" or Visit ID is not "1"', $readable); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The segment 'noTexisTinG' does not exist + */ + public function test_getHumanReadable_ShouldThrowAnException_IfTheGivenSegmentNameDoesNotExist() + { + $this->formatter->getHumanReadable($segment = 'noTexisTinG==1.0', $this->idSite); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage The segment 'pageUrl=!1.0' is not valid. + */ + public function test_getHumanReadable_ShouldThrowAnException_IfSegmentCannotBeParsedBecauseOfInvalidFormat() + { + $invalidOperator = '=!'; + $this->formatter->getHumanReadable($segment = 'pageUrl' . $invalidOperator . '1.0', $this->idSite); + } + +} diff --git a/plugins/SegmentEditor/tests/Integration/SegmentListTest.php b/plugins/SegmentEditor/tests/Integration/SegmentListTest.php new file mode 100644 index 0000000000..3a90bb95ee --- /dev/null +++ b/plugins/SegmentEditor/tests/Integration/SegmentListTest.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\SegmentEditor\tests\Integration; + +use Piwik\Plugins\SegmentEditor\SegmentList; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Exception; + +/** + * @group SegmentListTest + * @group SegmentList + * @group SegmentEditor + * @group Plugins + */ +class SegmentListTest extends IntegrationTestCase +{ + /** + * @var SegmentList + */ + private $list; + + private $idSite; + + public function setUp() + { + parent::setUp(); + + $this->idSite = Fixture::createWebsite('2012-01-01 00:00:00'); + $this->list = new SegmentList(); + } + + public function test_findSegment_shouldFindSegmentByName_IfNameExists() + { + $segmentName = 'pageUrl'; + + $segment = $this->list->findSegment($segmentName, $this->idSite); + $this->assertInternalType('array', $segment); + $this->assertSame($segmentName, $segment['segment']); + } + + public function test_findSegment_shouldNotFindSegmentByName_IfNameDoesNotExist() + { + $segment = $this->list->findSegment('aNyNotExisTinGSegmEnt', $this->idSite); + $this->assertNull($segment); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage checkUserHasViewAccess + */ + public function test_findSegment_ShouldThrowException_IfNotEnoughPermission() + { + FakeAccess::clearAccess($superUser = false, array(1)); + + $segment = $this->list->findSegment('pageUrl', 999); + $this->assertNull($segment); + } + + public function provideContainerConfig() + { + return array( + 'Piwik\Access' => new FakeAccess() + ); + } + +} diff --git a/plugins/SitesManager/API.php b/plugins/SitesManager/API.php index 4a67c45c2c..739af7d5e9 100644 --- a/plugins/SitesManager/API.php +++ b/plugins/SitesManager/API.php @@ -585,7 +585,7 @@ class API extends \Piwik\Plugin\API } if (!empty($settings)) { - $this->validateMeasurableSettings($bind['type'], $settings); + $this->validateMeasurableSettings(0, $bind['type'], $settings); } $idSite = $this->getModel()->createSite($bind); @@ -611,9 +611,9 @@ class API extends \Piwik\Plugin\API return (int) $idSite; } - private function validateMeasurableSettings($idType, $settings) + private function validateMeasurableSettings($idSite, $idType, $settings) { - $measurableSettings = new MeasurableSettings(0, $idType); + $measurableSettings = new MeasurableSettings($idSite, $idType); foreach ($measurableSettings->getSettingsForCurrentUser() as $measurableSetting) { $name = $measurableSetting->getName(); @@ -645,6 +645,7 @@ class API extends \Piwik\Plugin\API { Site::clearCache(); Cache::regenerateCacheWebsiteAttributes($idSite); + Cache::clearCacheGeneral(); SiteUrls::clearSitesCache(); } @@ -1184,7 +1185,7 @@ class API extends \Piwik\Plugin\API } if (!empty($settings)) { - $this->validateMeasurableSettings(Site::getTypeFor($idSite), $settings); + $this->validateMeasurableSettings($idSite, Site::getTypeFor($idSite), $settings); } $this->getModel()->updateSite($bind, $idSite); diff --git a/plugins/SitesManager/Model.php b/plugins/SitesManager/Model.php index 93ddb01d8f..96062b608a 100644 --- a/plugins/SitesManager/Model.php +++ b/plugins/SitesManager/Model.php @@ -286,6 +286,22 @@ class Model return $urls; } + /** + * Returns the list of alias URLs registered for the given idSite. + * The website ID must be valid when calling this method! + * + * @param int $idSite + * @return array list of alias URLs + */ + public function getAllKnownUrlsForAllSites() + { + $db = $this->getDb(); + $mainUrls = $db->fetchAll("SELECT idsite, main_url as url FROM " . Common::prefixTable("site")); + $aliasUrls = $db->fetchAll("SELECT idsite, url FROM " . Common::prefixTable("site_url")); + + return array_merge($mainUrls, $aliasUrls); + } + public function updateSite($site, $idSite) { $idSite = (int) $idSite; diff --git a/plugins/SitesManager/SiteUrls.php b/plugins/SitesManager/SiteUrls.php index a1fac67a89..33e2e4844a 100644 --- a/plugins/SitesManager/SiteUrls.php +++ b/plugins/SitesManager/SiteUrls.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\SitesManager; use Piwik\Cache; +use Piwik\Common; class SiteUrls { @@ -19,6 +20,117 @@ class SiteUrls self::getCache()->delete(self::$cacheId); } + /** + * Groups all URLs by host, path and idsite. + * + * @param array $urls An array containing URLs by idsite, + * eg array(array($idSite = 1 => array('apache.piwik', 'apache2.piwik'), 2 => array(), ...)) + * as returned by {@link getAllCachedSiteUrls()} and {@link getAllSiteUrls} + * @return array All urls grouped by host => path => idSites. Path having the most '/' will be listed first + * array( + 'apache.piwik' => array( + '/test/two' => $idsite = array(3), + '/test' => $idsite = array(1), + '/' => $idsite = array(2), + ), + 'test.apache.piwik' => array( + '/test/two' => $idsite = array(3), + '/test' => $idsite = array(1), + '/' => $idsite = array(2, 3), + ), + ); + */ + public function groupUrlsByHost($siteUrls) + { + if (empty($siteUrls)) { + return array(); + } + + $allUrls = array(); + + foreach ($siteUrls as $idSite => $urls) { + $idSite = (int) $idSite; + foreach ($urls as $url) { + $urlParsed = @parse_url($url); + + if ($urlParsed === false || !isset($urlParsed['host'])) { + continue; + } + + $host = $this->toCanonicalHost($urlParsed['host']); + $path = $this->getCanonicalPathFromParsedUrl($urlParsed); + + if (!isset($allUrls[$host])) { + $allUrls[$host] = array(); + } + + if (!isset($allUrls[$host][$path])) { + $allUrls[$host][$path] = array(); + } + + if (!in_array($idSite, $allUrls[$host][$path])) { + $allUrls[$host][$path][] = $idSite; + } + } + } + + foreach ($allUrls as $host => $paths) { + uksort($paths, array($this, 'sortByPathDepth')); + $allUrls[$host] = $paths; + } + + return $allUrls; + } + + public function getIdSitesMatchingUrl($parsedUrl, $urlsGroupedByHost) + { + if (empty($parsedUrl['host'])) { + return null; + } + + $urlHost = $this->toCanonicalHost($parsedUrl['host']); + $urlPath = $this->getCanonicalPathFromParsedUrl($parsedUrl); + + $matchingSites = null; + if (isset($urlsGroupedByHost[$urlHost])) { + $paths = $urlsGroupedByHost[$urlHost]; + + foreach ($paths as $path => $idSites) { + if (0 === strpos($urlPath, $path)) { + $matchingSites = $idSites; + break; + } + } + + if (!isset($matchingSites) && isset($paths['/'])) { + $matchingSites = $paths['/']; + } + } + + return $matchingSites; + } + + public function getPathMatchingUrl($parsedUrl, $urlsGroupedByHost) + { + if (empty($parsedUrl['host'])) { + return null; + } + + $urlHost = $this->toCanonicalHost($parsedUrl['host']); + $urlPath = $this->getCanonicalPathFromParsedUrl($parsedUrl); + + $matchingSites = null; + if (isset($urlsGroupedByHost[$urlHost])) { + $paths = $urlsGroupedByHost[$urlHost]; + + foreach ($paths as $path => $idSites) { + if (0 === strpos($urlPath, $path)) { + return $path; + } + } + } + } + public function getAllCachedSiteUrls() { $cache = $this->getCache(); @@ -35,23 +147,66 @@ class SiteUrls public function getAllSiteUrls() { $model = new Model(); - $siteIds = $model->getSitesId(); - $siteUrls = array(); + $siteUrls = $model->getAllKnownUrlsForAllSites(); - if (empty($siteIds)) { + if (empty($siteUrls)) { return array(); } - foreach ($siteIds as $siteId) { - $siteId = (int) $siteId; - $siteUrls[$siteId] = $model->getSiteUrlsFromId($siteId); + $urls = array(); + foreach ($siteUrls as $siteUrl) { + $siteId = (int) $siteUrl['idsite']; + + if (!isset($urls[$siteId])) { + $urls[$siteId] = array(); + } + + $urls[$siteId][] = $siteUrl['url']; } - return $siteUrls; + return $urls; } private static function getCache() { return Cache::getLazyCache(); } + + private function sortByPathDepth($pathA, $pathB) + { + // list first the paths with most '/' , and list path = '/' last + $numSlashA = substr_count($pathA, '/'); + $numSlashB = substr_count($pathB, '/'); + + if ($numSlashA === $numSlashB) { + return -1 * strcmp($pathA, $pathB); + } + + return $numSlashA > $numSlashB ? -1 : 1; + } + + private function toCanonicalHost($host) + { + $host = Common::mb_strtolower($host); + if (strpos($host, 'www.') === 0) { + $host = substr($host, 4); + } + + return $host; + } + + private function getCanonicalPathFromParsedUrl($urlParsed) + { + $path = '/'; + + if (isset($urlParsed['path'])) { + $path = Common::mb_strtolower($urlParsed['path']); + if (!Common::stringEndsWith($path, '/')) { + $path .= '/'; + } + } + + return $path; + } + } diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php index 4b686e2bde..ca3f2757b9 100644 --- a/plugins/SitesManager/SitesManager.php +++ b/plugins/SitesManager/SitesManager.php @@ -114,8 +114,11 @@ class SitesManager extends \Piwik\Plugin { $idSite = (int) $idSite; + $urls = API::getInstance()->getSiteUrlsFromId($idSite); + // add the 'hosts' entry in the website array - $array['hosts'] = $this->getTrackerHosts($idSite); + $array['urls'] = $urls; + $array['hosts'] = $this->getTrackerHosts($urls); $website = API::getInstance()->getSiteFromId($idSite); $array['exclude_unknown_urls'] = $website['exclude_unknown_urls']; @@ -252,9 +255,8 @@ class SitesManager extends \Piwik\Plugin * @param int $idSite * @return array */ - private function getTrackerHosts($idSite) + private function getTrackerHosts($urls) { - $urls = API::getInstance()->getSiteUrlsFromId($idSite); $hosts = array(); foreach ($urls as $url) { $url = parse_url($url); diff --git a/plugins/SitesManager/config/config.php b/plugins/SitesManager/config/config.php deleted file mode 100644 index 0855c39329..0000000000 --- a/plugins/SitesManager/config/config.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -return array( - - 'tracker.request.processors' => DI\add(array( - DI\get('Piwik\Plugins\SitesManager\Tracker\SitesManagerRequestProcessor'), - )), - -); diff --git a/plugins/SitesManager/lang/cs.json b/plugins/SitesManager/lang/cs.json index 014c9f88f2..6854eadf75 100644 --- a/plugins/SitesManager/lang/cs.json +++ b/plugins/SitesManager/lang/cs.json @@ -68,13 +68,13 @@ "SiteWithoutDataTitle": "Zatím nebyla zaznamenána žádná data", "SiteWithoutDataDescription": "Pro tuto stránku nebyla zatím zaznamenána žádná analytická data.", "SiteWithoutDataSetupTracking": "Prosím, nastavte %1$ssledovací javascriptový kód %2$s na vašich webových stránkách, a pak stránku obnovte.", - "SuperUserAccessCan": "eUživatel se super uživatelským přístupem může také %sspecifikovat globální nastavení%s pro nové webové stránky.", + "SuperUserAccessCan": "Uživatel se super uživatelským přístupem může také %sspecifikovat globální nastavení%s pro nové webové stránky.", "Timezone": "Časová zóna", "TrackingSiteSearch": "Sledování interního vyhledávání na stránkách", "TrackingTags": "Zaznamenávací tagy pro %s", "Urls": "URL", "UTCTimeIs": "UTC čas je %s", - "OnlyMatchedUrlsAllowed": "Sledovat návštěvy a akce pouze tehdy, když URL akce začíná jednou z výše uvedených URL.", + "OnlyMatchedUrlsAllowed": "Sledovat návštěvy a akce pouze tehdy, pokud URL akce začíná jednou z výše uvedených URL.", "OnlyMatchedUrlsAllowedHelp": "Pokud je povoleno, Piwik bude sledovat interní akce pouze tehdy, když je URL stránky jednou ze známých URL vašich webových stránek. To zabrání lidem, aby zahltili analýzu URL jiných stránek.", "WebsitesManagement": "Nastavení Web sídel", "XManagement": "Spravovat %s", diff --git a/plugins/SitesManager/lang/fr.json b/plugins/SitesManager/lang/fr.json index 283f77c0f3..2372187f6c 100644 --- a/plugins/SitesManager/lang/fr.json +++ b/plugins/SitesManager/lang/fr.json @@ -74,8 +74,8 @@ "TrackingTags": "Code de suivi pour %s", "Urls": "URLs", "UTCTimeIs": "L'heure UTC est %s.", - "OnlyMatchedUrlsAllowed": "Effectue le suivit des visites et actions uniquement quand l'URL d'action commence avec une des URL's ci-dessus.", - "OnlyMatchedUrlsAllowedHelp": "Quand activé, Piwik va effectuer le suivit des actions uniquement quand l'URL de la page est une des URL's de votre site web. Ceci empêche les gens de spammer vos données d'analyse avec des URL's d'autres sites web.", + "OnlyMatchedUrlsAllowed": "Effectue le suivi des visites et actions uniquement quand l'URL d'action commence avec une des URLs ci-dessus.", + "OnlyMatchedUrlsAllowedHelp": "Quand activé, Piwik va effectuer le suivi des actions uniquement quand l'URL de la page est une des URLs de votre site web. Ceci empêche les gens de spammer vos données d'analyse avec des URLs d'autres sites web.", "WebsitesManagement": "Gestion des sites", "XManagement": "Gérer %s", "ChooseMeasurableTypeHeadline": "Que voudriez-vous mesurer ?", diff --git a/plugins/SitesManager/lang/ja.json b/plugins/SitesManager/lang/ja.json index 56cc2b4697..b1d3a73a83 100644 --- a/plugins/SitesManager/lang/ja.json +++ b/plugins/SitesManager/lang/ja.json @@ -1,6 +1,7 @@ { "SitesManager": { "AddSite": "新しいサイトの追加", + "AddMeasurable": "新規の測定対象を追加", "AdvancedTimezoneSupportNotFound": "このサーバーの PHP では拡張タイムゾーンがサポートされていません(PHP>=5.2 でサポート)。 手作業で UTC オフセットを指定します。", "AliasUrlHelp": "これは推奨されますが、必須ではありません。 ビジターがこのウェブサイトのアクセスに使用するさまざまな URL を指定するには、1行に1つの URL を入力します。 ウェブサイトの URL エイリアスは、[参照元]>[ウェブサイト]のリポートには表示されません。 URL の 'www' の有無は、Piwik が両方とも考慮するため、指定する必要がないことに注意してください。", "ChangingYourTimezoneWillOnlyAffectDataForward": "タイムゾーンの変更は、今後のデータにのみ反映され、過去のデータには適用されません。", @@ -49,6 +50,7 @@ "OnlyOneSiteAtTime": "一度にウェブサイトは一つだけ編集できます。ウェブサイト %s への変更を保存するか、キャンセルしてください。", "PiwikOffersEcommerceAnalytics": "Piwikは、高度なeコマース分析、トラッキング&リポーティング、が可能になります。%s eコマース分析 %s について詳しく知る。", "PiwikWillAutomaticallyExcludeCommonSessionParameters": "一般的なセッションパラメータ(%s)は、Piwik が自動的に除外します。", + "PluginDescription": "Web サイト管理では、新しい web サイトを追加し、既存の web サイトを編集することができます。", "SearchCategoryDesc": "Piwik は、各内部サイト内検索キーワードに対する検索カテゴリを追跡することもできます。", "SearchCategoryLabel": "カテゴリーパラメーター", "SearchCategoryParametersDesc": "検索カテゴリを指定するクエリパラメーターリストを、コンマ区切りで入力できます。", @@ -72,7 +74,11 @@ "TrackingTags": "%s 用トラッキングタグ", "Urls": "URL", "UTCTimeIs": "UTC 時間は %s です。", + "OnlyMatchedUrlsAllowed": "アクション URL は、上記のいずれかの URLで始まる場合にのみビジットやアクションを追跡します。", + "OnlyMatchedUrlsAllowedHelp": "有効にすると、Piwik はページ URL があなたのウェブサイトの URL として知られたものである時に内部のアクションを追跡します。これは他のウェブサイトの URL を使用して分析をスパムすることから人々を防ぎます。", "WebsitesManagement": "ウェブサイトの管理", + "XManagement": "管理 %s", + "ChooseMeasurableTypeHeadline": "何を測定しますか?", "YouCurrentlyHaveAccessToNWebsites": "現在 %s つのウェブサイトに接続しています。", "YourCurrentIpAddressIs": "あなたの現在の IP アドレスは %s です。" } diff --git a/plugins/SitesManager/tests/Integration/ModelTest.php b/plugins/SitesManager/tests/Integration/ModelTest.php index 45ebce2be8..7291ac346f 100644 --- a/plugins/SitesManager/tests/Integration/ModelTest.php +++ b/plugins/SitesManager/tests/Integration/ModelTest.php @@ -56,10 +56,56 @@ class ModelTest extends IntegrationTestCase $this->assertSame(array('website', 'universal', 'mobileapp'), $this->model->getUsedTypeIds()); } - private function createMeasurable($type) + public function test_getAllKnownUrlsForAllSites_shouldReturnAllUrls() { - Fixture::createWebsite('2015-01-01 00:00:00', - $ecommerce = 0, $siteName = false, $siteUrl = false, + $idSite = $this->createMeasurable('website', 'http://apache.piwik'); + $this->model->insertSiteUrl($idSite, 'http://example.apache.piwik'); + $this->model->insertSiteUrl($idSite, 'http://example.org'); + + $idSite2 = $this->createMeasurable('website'); + $this->model->insertSiteUrl($idSite2, 'http://example.org'); + $this->model->insertSiteUrl($idSite2, 'http://example.com'); + + $idSite3 = $this->createMeasurable('website', 'http://example.pro'); + + $expected = array( + array( + 'idsite' => $idSite, + 'url' => 'http://apache.piwik' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://piwik.net' + ), + array( + 'idsite' => $idSite3, + 'url' => 'http://example.pro' + ), + array( + 'idsite' => $idSite, + 'url' => 'http://example.apache.piwik' + ), + array( + 'idsite' => $idSite, + 'url' => 'http://example.org' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://example.com' + ), + array( + 'idsite' => $idSite2, + 'url' => 'http://example.org' + ) + + ); + $this->assertEquals($expected, $this->model->getAllKnownUrlsForAllSites()); + } + + private function createMeasurable($type, $siteUrl = false) + { + return Fixture::createWebsite('2015-01-01 00:00:00', + $ecommerce = 0, $siteName = false, $siteUrl, $siteSearch = 1, $searchKeywordParameters = null, $searchCategoryParameters = null, $timezone = null, $type); } diff --git a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php index f9362ae2f4..3db34199fb 100644 --- a/plugins/SitesManager/tests/Integration/SiteUrlsTest.php +++ b/plugins/SitesManager/tests/Integration/SiteUrlsTest.php @@ -119,6 +119,176 @@ class SiteUrlsTest extends IntegrationTestCase $this->assertEquals($urlsToFake, $actual); } + public function test_groupUrlsByHost_shouldReturnEmptyArray_WhenNoUrlsGiven() + { + $this->assertSame(array(), $this->siteUrls->groupUrlsByHost(array())); + $this->assertSame(array(), $this->siteUrls->groupUrlsByHost(null)); + } + + public function test_groupUrlsByHost_shouldGroupByHost_WithOneSiteAndDifferentDomains_shouldRemoveWwwAndDefaultToPathSlash() + { + $idSite = 1; + $oneSite = array( + $idSite => array( + 'http://apache.piwik', + 'http://www.example.com', // should remove www. + 'https://example.org', // should handle https or other protocol + 'http://apache.piwik/', // same as initial one but with slash at the end, should not add idsite twice + 'http://third.www.com' // should not remove www. in the middle of a domain + ) + ); + + $expected = array( + 'apache.piwik' => array('/' => array($idSite)), + 'example.com' => array('/' => array($idSite)), + 'example.org' => array('/' => array($idSite)), + 'third.www.com' => array('/' => array($idSite)), + ); + + $this->assertSame($expected, $this->siteUrls->groupUrlsByHost($oneSite)); + } + + public function test_groupUrlsByHost_shouldGroupByHost_WithDifferentDomainsAndPathsShouldListPathByNumberOfDirectoriesAndConvertToLowerCase() + { + $idSite = 1; + $idSite2 = 2; + $idSite3 = 3; + $idSite4 = 4; + $idSite5 = 5; + + $urls = array( + $idSite => array( + 'http://apache.piwik/test', 'http://apache.piWik', 'http://apache.piwik/foo/bAr/', 'http://apache.piwik/Foo/SECOND' + ), + $idSite2 => array( + 'http://apache.piwik/test/', 'http://example.oRg', 'http://apache.piwik/foo/secOnd' + ), + $idSite3 => array( + 'http://apache.piwik/', 'http://apache.piwik/third', 'http://exampLe.com', 'http://example.org/foo/test/two' + ), + $idSite4 => array(), + $idSite5 => array('invalidUrl', 'ftp://example.org/'), + ); + + $expected = array( + 'apache.piwik' => array( + '/foo/second/' => array($idSite, $idSite2), + '/foo/bar/' => array($idSite), + '/third/' => array($idSite3), + '/test/' => array($idSite, $idSite2), + '/' => array($idSite, $idSite3) + ), + 'example.org' => array( + '/foo/test/two/' => array($idSite3), + '/' => array($idSite2, $idSite5) + ), + 'example.com' => array( + '/' => array($idSite3) + ), + ); + + $this->assertSame($expected, $this->siteUrls->groupUrlsByHost($urls)); + } + + /** + * @dataProvider getTestIdSitesMatchingUrl + */ + public function test_getIdSitesMatchingUrl($expectedMatchSites, $parsedUrl) + { + $urlsGroupedByHost = array( + 'apache.piwik' => array( + '/foo/second/' => array(2), + '/foo/sec/' => array(4), + '/foo/bar/' => array(1), + '/third/' => array(3), + '/test/' => array(1, 2), + '/' => array(1, 3) + ), + 'example.org' => array( + '/foo/test/two/' => array(3), + '/foo/second/' => array(6), + '/' => array(2, 5) + ), + 'example.com' => array( + '/' => array(3) + ), + ); + $matchedSites = $this->siteUrls->getIdSitesMatchingUrl($parsedUrl, $urlsGroupedByHost); + + $this->assertSame($expectedMatchSites, $matchedSites); + } + + public function getTestIdSitesMatchingUrl() + { + return array( + array(array(1,3), array('host' => 'apache.piwik')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => 'nomatch')), // no other URL matches a site so we fall back to domain match + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/nomatch')), + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second')), + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second/')), // it shouldn't matter if slash is at end or not + array(array(2), array('host' => 'apache.piwik', 'path' => '/foo/second/test')), // it should find best match + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec/test')), // it should not use /foo/second for these + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec/')), + array(array(4), array('host' => 'apache.piwik', 'path' => '/foo/sec')), + array(array(1,3), array('host' => 'apache.piwik', 'path' => '/foo')), + array(array(2,5), array('host' => 'example.org')), + array(array(2,5), array('host' => 'example.org', 'path' => '/')), + array(array(2,5), array('host' => 'example.org', 'path' => 'any/nonmatching/path')), + array(array(6), array('host' => 'example.org', 'path' => '/foo/second')), + array(array(6), array('host' => 'example.org', 'path' => '/foo/second/test')), + array(array(3), array('host' => 'example.com')), + array(null, array('host' => 'example.pro')), + array(null, array('host' => 'example.pro', 'path' => '/any')), + ); + } + + /** + * @dataProvider getTestPathMatchingUrl + */ + public function test_getPathMatchingUrl($expectedMatchSites, $parsedUrl) + { + $urlsGroupedByHost = array( + 'apache.piwik' => array( + '/foo/second/' => array(2), + '/foo/sec/' => array(4), + '/foo/bar/' => array(1), + '/third/' => array(3), + '/test/' => array(1, 2), + '/' => array(1, 3) + ), + 'example.org' => array( + '/foo/test/two/' => array(3), + '/foo/second/' => array(6), + '/' => array(2, 5) + ), + ); + $matchedSites = $this->siteUrls->getPathMatchingUrl($parsedUrl, $urlsGroupedByHost); + + $this->assertSame($expectedMatchSites, $matchedSites); + } + + public function getTestPathMatchingUrl() + { + return array( + array('/', array('host' => 'apache.piwik')), + array('/', array('host' => 'apache.piwik', 'path' => '/')), + array('/', array('host' => 'apache.piwik', 'path' => '')), + array(null, array('host' => 'test.piwik')), + array(null, array('host' => 'test.apache.piwik')), // we do not match subdomains, only exact domain match + + array('/foo/bar/', array('host' => 'apache.piwik', 'path' => '/foo/bar')), + array('/foo/bar/', array('host' => 'apache.piwik', 'path' => '/foo/bar/')), + array('/foo/bar/', array('host' => 'apache.piwik', 'path' => '/foo/bar/baz/')), + array('/', array('host' => 'apache.piwik', 'path' => '/foo/baz/bar/')), + array('/third/', array('host' => 'apache.piwik', 'path' => '/third/bar/baz/')), + + array('/foo/second/', array('host' => 'example.org', 'path' => '/foo/second/')), + array('/', array('host' => 'example.org', 'path' => '/foo/secon')), + array(null, array('host' => 'example.pro', 'path' => '/foo/second/')), + ); + } + private function assertSiteUrls($expectedUrls) { $urls = $this->siteUrls->getAllSiteUrls(); diff --git a/plugins/TasksTimetable b/plugins/TasksTimetable -Subproject 32915362e3f90f8e98929108ccc13695a820c2f +Subproject 67c1119ff385f153439e81555d7d3e4a799ebe5 diff --git a/plugins/TestRunner/Commands/GenerateTravisYmlFile.php b/plugins/TestRunner/Commands/GenerateTravisYmlFile.php index 5e1ac7d181..a99c3ae4f0 100644 --- a/plugins/TestRunner/Commands/GenerateTravisYmlFile.php +++ b/plugins/TestRunner/Commands/GenerateTravisYmlFile.php @@ -39,7 +39,10 @@ class GenerateTravisYmlFile extends ConsoleCommand ->addOption('php-versions', null, InputOption::VALUE_OPTIONAL, "List of PHP versions to test against, ie, 5.4,5.5,5.6. Defaults to: 5.3.3,5.4,5.5,5.6.") ->addOption('dump', null, InputOption::VALUE_REQUIRED, "Debugging option. Saves the output .travis.yml to the specified file.") - ->addOption('repo-root-dir', null, InputOption::VALUE_REQUIRED, "Path to the repo for whom a .travis.yml file will be generated for."); + ->addOption('repo-root-dir', null, InputOption::VALUE_REQUIRED, "Path to the repo for whom a .travis.yml file will be generated for.") + ->addOption('force-php-tests', null, InputOption::VALUE_NONE, "Forces the presence of the PHP tests jobs for plugin builds.") + ->addOption('force-ui-tests', null, InputOption::VALUE_NONE, "Forces the presence of the UI tests jobs for plugin builds.") + ->addOption('sudo-false', null, InputOption::VALUE_NONE, "If supplied, the .travis.yml file will use travis' new infrastructure."); } protected function execute(InputInterface $input, OutputInterface $output) diff --git a/plugins/TestRunner/Commands/TestsRun.php b/plugins/TestRunner/Commands/TestsRun.php index 08e456da19..8a12c07e3b 100644 --- a/plugins/TestRunner/Commands/TestsRun.php +++ b/plugins/TestRunner/Commands/TestsRun.php @@ -47,10 +47,6 @@ class TestsRun extends ConsoleCommand // bin is the composer executeable directory, where all vendors (should) place their executables $command = PIWIK_VENDOR_PATH . '/bin/phpunit'; - if (version_compare(PHP_VERSION, '5.4.0', '<')) { - $command = 'php -dzend.enable_gc=0 ' . $command; - } - if (!$this->isCoverageEnabled($options) && $this->isXdebugLoaded()) { $message = 'Did you know? You can run tests faster by disabling xdebug'; if($this->isXdebugCodeCoverageEnabled()) { diff --git a/plugins/TestRunner/Commands/TestsRunUI.php b/plugins/TestRunner/Commands/TestsRunUI.php index faf436a8af..3a9a06c080 100644 --- a/plugins/TestRunner/Commands/TestsRunUI.php +++ b/plugins/TestRunner/Commands/TestsRunUI.php @@ -26,7 +26,7 @@ class TestsRunUI extends ConsoleCommand \nRun one spec: \n./console tests:run-ui UIIntegrationTest "); - $this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by comma, for instance core,integration', array()); + $this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by a space, for instance UIIntegrationTest ', array()); $this->addOption("persist-fixture-data", null, InputOption::VALUE_NONE, "Persist test data in a database and do not execute tear down."); $this->addOption('keep-symlinks', null, InputOption::VALUE_NONE, "Keep recursive directory symlinks so test pages can be viewed in a browser."); $this->addOption('print-logs', null, InputOption::VALUE_NONE, "Print webpage logs even if tests succeed."); @@ -35,6 +35,9 @@ class TestsRunUI extends ConsoleCommand $this->addOption('plugin', null, InputOption::VALUE_REQUIRED, "Execute all tests for a plugin."); $this->addOption('core', null, InputOption::VALUE_NONE, "Execute only tests for Piwik core & core plugins."); $this->addOption('skip-delete-assets', null, InputOption::VALUE_NONE, "Skip deleting of merged assets (will speed up a test run, but not by a lot)."); + $this->addOption('screenshot-repo', null, InputOption::VALUE_NONE, "For tests"); + $this->addOption('store-in-ui-tests-repo', null, InputOption::VALUE_NONE, "For tests"); + $this->addOption('extra-options', null, InputOption::VALUE_REQUIRED, "Extra options to pass to phantomjs."); } protected function execute(InputInterface $input, OutputInterface $output) @@ -48,6 +51,9 @@ class TestsRunUI extends ConsoleCommand $plugin = $input->getOption('plugin'); $skipDeleteAssets = $input->getOption('skip-delete-assets'); $core = $input->getOption('core'); + $extraOptions = $input->getOption('extra-options'); + $storeInUiTestsRepo = $input->getOption('store-in-ui-tests-repo'); + $screenshotRepo = $input->getOption('screenshot-repo'); if (!$skipDeleteAssets) { AssetManager::getInstance()->removeMergedAssets(); @@ -84,6 +90,18 @@ class TestsRunUI extends ConsoleCommand $options[] = "--core"; } + if ($storeInUiTestsRepo) { + $options[] = "--store-in-ui-tests-repo"; + } + + if ($screenshotRepo) { + $options[] = "--screenshot-repo"; + } + + if ($extraOptions) { + $options[] = $extraOptions; + } + $options = implode(" ", $options); $specs = implode(" ", $specs); @@ -93,7 +111,9 @@ class TestsRunUI extends ConsoleCommand $output->writeln('Executing command: <info>' . $cmd . '</info>'); $output->writeln(''); - passthru($cmd); + passthru($cmd, $returnCode); + + return $returnCode; } /** diff --git a/plugins/TestRunner/Commands/TestsSetupFixture.php b/plugins/TestRunner/Commands/TestsSetupFixture.php index 510578cb76..565a81122a 100644 --- a/plugins/TestRunner/Commands/TestsSetupFixture.php +++ b/plugins/TestRunner/Commands/TestsSetupFixture.php @@ -154,7 +154,7 @@ class TestsSetupFixture extends ConsoleCommand private function createSymbolicLinksForUITests() { // make sure symbolic links exist (phantomjs doesn't support symlink-ing yet) - foreach (array('libs', 'plugins', 'tests', 'piwik.js') as $linkName) { + foreach (array('libs', 'plugins', 'tests', 'misc', 'piwik.js') as $linkName) { $linkPath = PIWIK_INCLUDE_PATH . '/tests/PHPUnit/proxy/' . $linkName; if (!file_exists($linkPath)) { symlink(PIWIK_INCLUDE_PATH . '/' . $linkName, $linkPath); @@ -192,10 +192,7 @@ class TestsSetupFixture extends ConsoleCommand ); foreach ($optionsToOverride as $configOption => $value) { if ($value) { - $configOverride = $testingEnvironment->configOverride; - $configOverride['database_tests'][$configOption] = $configOverride['database'][$configOption] = $value; - $testingEnvironment->configOverride = $configOverride; - + $testingEnvironment->overrideConfig('database_tests', $configOption, $value); Config::getInstance()->database[$configOption] = $value; } } @@ -212,6 +209,7 @@ class TestsSetupFixture extends ConsoleCommand throw new \Exception("Cannot find fixture class '$fixtureClass'."); } + /** @var Fixture $fixture */ $fixture = new $fixtureClass(); $fixture->printToScreen = true; @@ -233,6 +231,8 @@ class TestsSetupFixture extends ConsoleCommand $fixture->extraPluginsToLoad = explode(',', $extraPluginsToLoad); } + $fixture->extraDiEnvironments = array('ui-test'); + return $fixture; } diff --git a/plugins/Transitions/javascripts/transitions.js b/plugins/Transitions/javascripts/transitions.js index cb98e3d045..8461c89069 100644 --- a/plugins/Transitions/javascripts/transitions.js +++ b/plugins/Transitions/javascripts/transitions.js @@ -17,8 +17,12 @@ function DataTable_RowActions_Transitions(dataTable) { DataTable_RowActions_Transitions.prototype = new DataTable_RowAction; /** Static helper method to launch transitions from anywhere */ -DataTable_RowActions_Transitions.launchForUrl = function (url) { - broadcast.propagateNewPopoverParameter('RowAction', 'Transitions:url:' + url); +DataTable_RowActions_Transitions.launchForUrl = function (url, segment) { + var value = 'Transitions:url:' + url; + if (segment) { + value += ':segment:' + segment; + } + broadcast.propagateNewPopoverParameter('RowAction', value); }; DataTable_RowActions_Transitions.isPageUrlReport = function (module, action) { @@ -30,19 +34,25 @@ DataTable_RowActions_Transitions.isPageTitleReport = function (module, action) { return module == 'Actions' && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch'); }; +DataTable_RowActions_Transitions.registeredReports = []; +DataTable_RowActions_Transitions.registerReport = function (handler) { + DataTable_RowActions_Transitions.registeredReports.push(handler); +} + DataTable_RowActions_Transitions.prototype.trigger = function (tr, e, subTableLabel) { - var link = tr.find('> td:first > a').attr('href'); - link = $('<textarea>').html(link).val(); // remove html entities - - var module = this.dataTable.param.module; - var action = this.dataTable.param.action; - if (DataTable_RowActions_Transitions.isPageUrlReport(module, action)) { - this.openPopover('url:' + link); - } else if (DataTable_RowActions_Transitions.isPageTitleReport(module, action)) { - DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]); - } else { - alert('Transitions can\'t be used on this report.'); + var i = 0; + for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) { + var report = DataTable_RowActions_Transitions.registeredReports[i]; + if (report + && report.trigger + && report.isAvailableOnReport + && report.isAvailableOnReport(this.dataTable.param)) { + report.trigger.apply(this, arguments); + return; + } } + + alert('Transitions can\'t be used on this report.'); }; DataTable_RowActions_Transitions.prototype.performAction = function (label, tr, e) { @@ -57,6 +67,15 @@ DataTable_RowActions_Transitions.prototype.performAction = function (label, tr, }; DataTable_RowActions_Transitions.prototype.doOpenPopover = function (link) { + var posSegment = (link+'').indexOf(':segment:'); + var segment = null; + + // handle and remove ':segment:$SEGMENT' from link + if (posSegment && posSegment > 0) { + segment = link.substring(posSegment + (':segment:'.length)); + link = link.substring(0, posSegment); + } + var parts = link.split(':'); if (parts.length < 2) { return; @@ -67,9 +86,9 @@ DataTable_RowActions_Transitions.prototype.doOpenPopover = function (link) { var actionName = parts.join(':'); if (this.transitions === null) { - this.transitions = new Piwik_Transitions(actionType, actionName, this); + this.transitions = new Piwik_Transitions(actionType, actionName, this, segment); } else { - this.transitions.reset(actionType, actionName); + this.transitions.reset(actionType, actionName, segment); } this.transitions.showPopover(); }; @@ -93,10 +112,17 @@ DataTable_RowActions_Registry.register({ }, isAvailableOnReport: function (dataTableParams) { - return ( - DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) || - DataTable_RowActions_Transitions.isPageTitleReport(dataTableParams.module, dataTableParams.action) - ); + var i = 0; + for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) { + var report = DataTable_RowActions_Transitions.registeredReports[i]; + if (report + && report.isAvailableOnReport + && report.isAvailableOnReport(dataTableParams)) { + return true; + } + } + + return false; }, isAvailableOnRow: function (dataTableParams, tr) { @@ -104,11 +130,18 @@ DataTable_RowActions_Registry.register({ // not available on groups (i.e. folders) return false; } - if (DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) - && !tr.find('> td:first span.label').parent().is('a')) { - // not on page url without link (i.e. "Page URL not defined") - return false; + + var i = 0; + for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) { + var report = DataTable_RowActions_Transitions.registeredReports[i]; + if (report + && report.isAvailableOnRow + && report.isAvailableOnReport + && report.isAvailableOnReport(dataTableParams)) { + return report.isAvailableOnRow(dataTableParams, tr); + } } + return true; } @@ -118,8 +151,8 @@ DataTable_RowActions_Registry.register({ // TRANSITIONS IMPLEMENTATION // -function Piwik_Transitions(actionType, actionName, rowAction) { - this.reset(actionType, actionName); +function Piwik_Transitions(actionType, actionName, rowAction, segment) { + this.reset(actionType, actionName, segment); this.rowAction = rowAction; this.ajax = new Piwik_Transitions_Ajax(); @@ -129,9 +162,10 @@ function Piwik_Transitions(actionType, actionName, rowAction) { this.rightGroups = ['followingPages', 'followingSiteSearches', 'downloads', 'outlinks']; } -Piwik_Transitions.prototype.reset = function (actionType, actionName) { +Piwik_Transitions.prototype.reset = function (actionType, actionName, segment) { this.actionType = actionType; this.actionName = actionName; + this.segment = segment; this.popover = null; this.canvas = null; @@ -179,7 +213,7 @@ Piwik_Transitions.prototype.showPopover = function () { } // load the data - self.model.loadData(self.actionType, self.actionName, function () { + self.model.loadData(self.actionType, self.actionName, self.segment, function () { if (typeof Piwik_Transitions.popoverHtml == 'undefined') { // html not there yet callbackForHtml = bothLoaded; @@ -1249,7 +1283,7 @@ Piwik_Transitions_Model.prototype.htmlLoaded = function () { }; }; -Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, callback) { +Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, segment, callback) { var self = this; this.pageviews = 0; @@ -1287,11 +1321,16 @@ Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, c this.date = ''; - this.ajax.callApi('Transitions.getTransitionsForAction', { - actionType: actionType, - actionName: actionName, - expanded: 1 - }, + var params = { + actionType: actionType, + actionName: actionName, + expanded: 1 + }; + if (segment) { + params.segment = segment; + } + + this.ajax.callApi('Transitions.getTransitionsForAction', params, function (report) { self.date = report.date; diff --git a/plugins/Transitions/lang/ko.json b/plugins/Transitions/lang/ko.json index 468c039e52..1acc59cff6 100644 --- a/plugins/Transitions/lang/ko.json +++ b/plugins/Transitions/lang/ko.json @@ -16,6 +16,7 @@ "NoDataForAction": "%s 데이터 없음", "NoDataForActionDetails": "특정 동작은 %s을 하는 기간 동안 페이지뷰가 일어나지 않거나 유효하지 않습니다.", "OutgoingTraffic": "나가는 트래픽", + "PluginDescription": "새로운 변화에 대한 보고서 내 각 페이지 URL에 대한 이전 및 다음의 보고서는 새 아이콘을 통한 동작 보고서에서 확인할 수 있습니다.", "ShareOfAllPageviews": "이 페이지의 페이지뷰 %s (전체 페이지뷰 수 %s)", "ToFollowingPages": "내부 페이지로", "ToFollowingPagesInline": "%s 내부 페이지", diff --git a/plugins/TreemapVisualization b/plugins/TreemapVisualization -Subproject 9640640f7b49374790330699c9985634ecb9c0a +Subproject e487d14390a4b99504a38ba6237705c10317b12 diff --git a/plugins/UserCountry/lang/cs.json b/plugins/UserCountry/lang/cs.json index d39b67ea08..821069f8d4 100644 --- a/plugins/UserCountry/lang/cs.json +++ b/plugins/UserCountry/lang/cs.json @@ -6,7 +6,7 @@ "CannotFindPeclGeoIPDb": "Nepodařilo se najít databázi zemí, regionů nebo měst pro GEOIP modul PECl. Ujistěte se, že vaše GEOIP databáze je umísěna v adresáři %1$s a je pojmenována %2$s nebo %3$s, jinak si jí GEOIP modul PECl nevšimne.", "CannotListContent": "Nepodařilo se vypsat obsah pro %1$s: %2$s", "CannotLocalizeLocalIP": "IP adresa %s je místní a enemůže být geolokována.", - "CannotSetupGeoIPAutoUpdating": "Vypadá to, že vaše GEOIP databáze ukládáte vně Piwiku (víme to, protože v podadresáři misc žádné databáze nejsou, ale gEOIP funguje). Piwik nemůže automaticky aktualizovat GEOIP databázi, když není uložena v podadresáři misc.", + "CannotSetupGeoIPAutoUpdating": "Vypadá to, že svoje GEOIP databáze ukládáte vně Piwiku (víme to, protože v podadresáři misc žádné databáze nejsou, ale gEOIP funguje). Piwik nemůže automaticky aktualizovat GEOIP databázi, pokud není uložena v podadresáři misc.", "CannotUnzipDatFile": "Nepodařilo se rozbalit dat soubor v %1$s: %2$s", "City": "Město", "CityAndCountry": "%1$s, %2$s", @@ -84,7 +84,7 @@ "Region": "Region", "SetupAutomaticUpdatesOfGeoIP": "Nastavit automatické aktualizace GeoIP databází", "SubmenuLocations": "Umístění", - "TestIPLocatorFailed": "Piwik se pokusil zjistit místění námé IP adresy %1$s, ale server odpověděl %2$s. Kdyby byl tento poskytovatel nastaven správně, odpověděl by %3$s.", + "TestIPLocatorFailed": "Piwik se pokusil zjistit místění známé IP adresy %1$s, ale server odpověděl %2$s. Pokud by byl tento poskytovatel nastaven správně, odpověděl by %3$s.", "ThisUrlIsNotAValidGeoIPDB": "Stažený soubor není platná GeoIP databáze. Zkontrolujte URL, nebo soubor stáhněte ručně.", "ToGeolocateOldVisits": "Pokud chcete získat data o umístění pro staré návštěvy, použijte skript popisovaný %1$szde%2$s.", "UnsupportedArchiveType": "Nalezen nepodporovaný typ archivu %1$s.", diff --git a/plugins/UserCountry/lang/id.json b/plugins/UserCountry/lang/id.json index 5a49080dfc..efc27e6e38 100644 --- a/plugins/UserCountry/lang/id.json +++ b/plugins/UserCountry/lang/id.json @@ -9,12 +9,14 @@ "CannotSetupGeoIPAutoUpdating": "Tampaknya Anda menyimpan basidata GeoIP Anda di luar Piwik (kami dapat katakan sejak tidak ada basisdata di subdirektori misc, tapi GeoIP Anda berkerja). Piwik tidak dapat otomatis memperbarui basisdata GeoIP Anda bila berada di luar direktori misc.", "CannotUnzipDatFile": "Tidak dapat melakukan unzip berkas dat di %1$s: %2$s", "City": "Kota", + "CityAndCountry": "%1$s, %2$s", "Continent": "Benua", "Country": "Negara", "country_a1": "Wali Anonim", "country_a2": "Penyedia Satelit", "country_cat": "Masyarakat berbahasa Katalan", "country_o1": "Negara Lain", + "country_ti": "Tibet", "CurrentLocationIntro": "Berdasar penyedia ini, lokasi Anda adalah", "DefaultLocationProviderDesc1": "Lokasi penyedia asali menebak negara pengunjung dari bahasa yang digunakan.", "DefaultLocationProviderDesc2": "Ini sangat tidak teliti, serta %1$skami menyarankan memasang dan menggunakan %2$sGeoIP%3$s.%4$s", @@ -23,6 +25,7 @@ "DownloadNewDatabasesEvery": "Perbarui basisdata setiap", "FatalErrorDuringDownload": "Galat fatal terjadi ketika mengunduh berkas ini. Kemungkinan ada yang salah dengan sambungan internet Anda, dengan basisdata GeoIP yang Anda unduh, atau Piwik. Silakan coba mengunduh dan memasang secara manual.", "FoundApacheModules": "Piwik menemukan modul Apache berikut", + "FromDifferentCities": "kota berbeda", "GeoIPCannotFindMbstringExtension": "Tidak dapat menemukan fungsi %1$s. Harap pastikan bahwa ekstensi %2$s telah terpasang dan termuat.", "GeoIPDatabases": "Basisdata GeoIP", "GeoIPDocumentationSuffix": "Agar dapat melihat data untuk laporan ini, Anda harus memasang GeoIP di tab pengelola Lokasi-Geo. Basisdata GeoIP %1$sMaxmind%2$s komersial lebih teliti daripada yang gratis. Untuk melihat seberapa teliti mereka, klik %3$sdi sini%4$s.", diff --git a/plugins/UserCountry/lang/ja.json b/plugins/UserCountry/lang/ja.json index f5d3a08214..303595ae43 100644 --- a/plugins/UserCountry/lang/ja.json +++ b/plugins/UserCountry/lang/ja.json @@ -16,6 +16,7 @@ "country_a2": "衛星プロバイダ", "country_cat": "カタロニア語圏のコミュニティ", "country_o1": "その他の国", + "country_ti": "チベット", "CurrentLocationIntro": "このプロバイダーによると、あなたの現在地は", "DefaultLocationProviderDesc1": "デフォルトロケーションプロバイダーでは、ビジターの国は使用言語に基づいて推測されます。", "DefaultLocationProviderDesc2": "これは正確ではありません。%1$swe recommend installing and using %2$sGeoIP%3$s。%4$s", diff --git a/plugins/UserCountry/lang/ko.json b/plugins/UserCountry/lang/ko.json index 13a2f18b53..0ef8232311 100644 --- a/plugins/UserCountry/lang/ko.json +++ b/plugins/UserCountry/lang/ko.json @@ -2,38 +2,44 @@ "UserCountry": { "AssumingNonApache": "아파치 웹서버가 아니어서 apache_get_modules을 찾을 수 없습니다.", "CannotFindGeoIPDatabaseInArchive": "%2$s tar 압축파일에서 %1$s 파일을 찾을 수 없습니다!", - "CannotFindGeoIPServerVar": "%s에 변수가 설정되어 있지 않습니다. 서버가 올바르게 구성되 않았을 수 있습니다.", + "CannotFindGeoIPServerVar": "%s에 변수가 설정되어 있지 않습니다. 서버가 올바르게 구성되지 않았을 수 있습니다.", "CannotFindPeclGeoIPDb": "GeoIP PECL 모듈에서 국가, 지역 또는 도시 데이터베이스를 찾을 수 없습니다. GeoIP 데이터베이스가 %1$s에 위치하고 파일 이름이 %2$s 또는 %3$s인지 확인하세요. 틀린 부분이 있다면 PECL 모듈을 인식하지 못합니다.", "CannotListContent": "%1$s에 대한 내용을 나열 할 수 없습니다: %2$s", "CannotLocalizeLocalIP": "IP 주소 %s는 로컬 주소이기 때문에 위치를 추적할 수 없습니다.", - "CannotSetupGeoIPAutoUpdating": "Piwik의 외부에서 GeoIP 데이터베이스를 저장한 것 같군요 (하위 디렉토리인 misc에 데이터베이스가 없지만, 당신의 GeoIP가 잘 작동하고 있어요) 이 파일들이 misc 디렉토리의 밖에 있는 경우, Piwik는 자동으로 GeoIP 데이터베이스를 업데이트할 수 없습니다.", + "CannotSetupGeoIPAutoUpdating": "Piwik의 외부에서 GeoIP 데이터베이스를 저장한 것 같군요 (하위 디렉토리인 misc에 데이터베이스가 없지만, 당신의 GeoIP가 잘 작동하고 있어요). 이 파일들이 misc 디렉토리의 밖에 있는 경우, Piwik는 자동으로 GeoIP 데이터베이스를 업데이트할 수 없습니다.", "CannotUnzipDatFile": "%1$s의 DAT 파일 압축을 풀 수 없습니다: %2$s", "City": "도시", + "CityAndCountry": "%1$s, %2$s", "Continent": "대륙", "Country": "국가", "country_a1": "익명 프록시", "country_a2": "위성 공급자", "country_cat": "카탈로니아 지역의 커뮤니티", "country_o1": "기타 국가", + "country_ti": "티베트", "CurrentLocationIntro": "이 공급자에 따르면, 현재 위치는", "DefaultLocationProviderDesc1": "방문자의 국가에 기반하는 언어에서 추측하는 기본 위치 공급자입니다.", "DefaultLocationProviderDesc2": "이것은 매우 부정확합니다. 그래서 %1$s우리는 %2$sGeoIP%3$s를 설치하여 사용하는 것을 추천합니다.%4$s", + "DefaultLocationProviderExplanation": "당신은 현재 기본 위치 공급자를 사용하고 있어, Piwik는 방문자의 위치를 그들이 사용하는 언어에 기반하여 추측하고 있습니다. %1$s이 문서%2$s에서 어떻게 더 정확한 위치를 얻어낼 수 있는지 얘기하고 있습니다.", "DistinctCountries": "%s개 국가", "DownloadingDb": "다운로드 중 %s", "DownloadNewDatabasesEvery": "모든 데이터베이스 업데이트", "FatalErrorDuringDownload": "파일을 다운로드하는 동안 치명적인 오류가 발생했습니다. Piwik에서 GeoIP 데이터베이스 다운로드하는데 인터넷 연결에 문제가 있을 수 있습니다, 직접 다운로드해서 수동으로 설치해 보세요.", "FoundApacheModules": "Piwik은 다음의 아파치 모듈을 찾을 수 없음", - "GeoIPCannotFindMbstringExtension": "%1$s 함수를 찾을 수 없습니다. %2$s 확장이 설치되었고 로드되었는지 확인하세요.", + "FromDifferentCities": "다른 도시", + "GeoIPCannotFindMbstringExtension": "%1$s 함수를 찾을 수 없습니다. %2$s 확장이 설치 및 로드되었는지 확인하세요.", "GeoIPDatabases": "GeoIP 데이터베이스", "GeoIPDocumentationSuffix": "이 보고서에 대한 데이터를 볼 수 있도록 당신은 위치 정보관리 탭에서 GeoIP를 설정해야합니다. 상업용 %1$sMaxmind%2$s GeoIP 데이터베이스는 무료로 버전보다 더 정확합니다. 정확도를 높이려면 %3$s공유%4$s를 클릭하세요.", "GeoIPImplHasAccessTo": "이 GeoIP 구현에서 접근하는 데이터베이스 유형은", + "GeoIPIncorrectDatabaseFormat": "당신이 사용하는 GeoIP 데이터베이스는 올바른 포멧을 가지고 있지 않는 듯싶습니다. 아마도 손상된 것으로 보입니다. 바이너리 버전을 사용하는지 확인하시고 다른 복사본으로 바꿔 다시 진행해보세요.", "GeoIpLocationProviderDesc_Pecl1": "이 위치 공급자는 PECL 모듈과 GeoIP 데이터베이스를 사용하여 정확하고 효율적으로 방문자의 위치를 결정합니다.", - "GeoIpLocationProviderDesc_Pecl2": "제한이 없는 공급자입니다. 그래서 우리는 이것이 사용되는 것을 추천합니다.", + "GeoIpLocationProviderDesc_Pecl2": "제한이 없는 공급자입니다. 그래서 우리는 이를 사용하는 것을 추천합니다.", "GeoIpLocationProviderDesc_Php1": "이 위치 공급자는 서버에 설치 및 구성을 요구하지 않기 때문에 가장 간단합니다 (공유 호스팅에 적합!). GeoIP 데이터베이스 및 MaxMind의 PHP API를 사용하여 정확하게 방문자의 위치를 결정합니다.", "GeoIpLocationProviderDesc_Php2": "웹사이트 트래픽이 높은 경우에 이 위치 공급자는 너무 느립니다. 이 경우는 %1$sPECL 확장%2$s이나 %3$s서버 모듈%4$s을 설치하는 것이 좋습니다.", "GeoIpLocationProviderDesc_ServerBased1": "이 위치 공급자는 HTTP 서버에 설치되는 GeoIP 모듈을 사용합니다. 이 것은 빠르고 정확하지만, %1$s일반 브라우저의 추적만 사용할 수 있습니다.%2$s", "GeoIpLocationProviderDesc_ServerBased2": "로그 파일을 가져오거나 IP 주소 설정을 필요로하는 경우 %1$sPECL GeoIP 구현 (권장)%2$s 또는 %3$sPHP GeoIP 구현%4$s을 사용합니다.", "GeoIpLocationProviderDesc_ServerBasedAnonWarn": "참고: IP를 익명화하면 공급자가 보고있는 위치에 아무런 영향을 미치지 않습니다. IP를 익명화 하지 않으면, 개인 정보 보호법을 위반이 될수 있으므로 주의하세요.", + "GeoIpLocationProviderNotRecomnended": "지리적 위치 플러그인은 동작하지만, 현재 당신은 추천하는 공급자를 사용하고 있지 않습니다.", "GeoIPNoServerVars": "Piwik은 GeoIP %s 변수를 찾을 수 없습니다.", "GeoIPPeclCustomDirNotSet": "%s PHP ini 옵션이 설정되어 있지 않습니다.", "GeoIPServerVarsFound": "Piwik은 다음의 GeoIP %s 변수를 감지함", @@ -52,7 +58,7 @@ "HowToInstallNginxModule": "Nginx에 GeoIP 모듈을 설치하려면 어떻게해야합니까?", "HowToSetupGeoIP": "GeoIP로 정확한 위치 정보를 설정하는 방법", "HowToSetupGeoIP_Step1": "%3$sMaxMind%4$s에서 GeoLite 도시 데이터베이스를 %1$s다운로드%2$s 합니다.", - "HowToSetupGeoIP_Step2": "이 파일의 압축을 풀고 %1$s 파일을 Piwik 하위 디렉토리인 %2$smisc%3$s로 복사합니다 (FTP나 SSH를 이용하여).", + "HowToSetupGeoIP_Step2": "(FTP나 SSH를 이용하여) 이 파일의 압축을 풀고 %1$s 파일을 Piwik 하위 디렉토리인 %2$smisc%3$s로 복사합니다.", "HowToSetupGeoIP_Step3": "화면을 새로 고침합니다. %1$sGeoIP (PHP)%2$s 공급자가 %3$s설치됨%4$s으로 나타날 것입니다. 이것을 선택합니다.", "HowToSetupGeoIP_Step4": "작업 완료! 이제 Piwik은 GeoIP를 사용하도록 설정되었으며, 이것은 방문자의 정확한 국가와 도시 지역 정보를 볼 수 있음을 의미합니다.", "HowToSetupGeoIPIntro": "위치 정보를 정확하게 설정하지 않으면 방문자의 위치 정보 및 이와 관련한 유용한 기능이 표시되지 않습니다. 다음 방법을 참고하여 어서 이 기능을 사용하세요:", @@ -64,7 +70,7 @@ "Latitude": "위도", "Location": "위치", "LocationDatabase": "위치 데이터베이스", - "LocationDatabaseHint": "위치 데이터베이스 중 하나 국가, 지역 또는 도시 데이터베이스입니다.", + "LocationDatabaseHint": "위치 데이터베이스는 국가, 지역 또는 도시 데이터베이스를 말합니다.", "LocationProvider": "위치 공급자", "Longitude": "경도", "NoDataForGeoIPReport1": "이 보고서에 대한 데이터가 없습니다. 어떠한 위치 데이터도 사용할 수 없거나, 방문자의 IP 주소에서 지역정보를 찾을 수 없기 때문입니다.", @@ -74,6 +80,7 @@ "PeclGeoIPNoDBDir": "PECL 모듈이 %1$s 경로에서 데이터베이스를 찾았지만, 디렉토리가 존재하지 않습니다. 디렉토리를 만들고 여기에 GeoIP 데이터베이스를 추가하세요. 대안으로, php.ini 파일에 올바른 디렉토리를 %2$s 경로를 설정할 수 있습니다.", "PeclGeoLiteError": "%1$s에 있는 GeoIP 데이터베이스 이름은 %2$s입니다. 안타깝게도, PECL 모듈은 이 이름으로 인식하지 않습니다. 이름을 %3$s로 변경해 주세요.", "PiwikNotManagingGeoIPDBs": "Piwik은 현재 모든 GeoIP 데이터베이스를 관리하지 않습니다.", + "PluginDescription": "방문자 위치 보고서: 국가, 지역, 도시, 지리적 좌표(위도\/경도)", "Region": "지역", "SetupAutomaticUpdatesOfGeoIP": "GeoIP 데이터베이스의 자동 업데이트 설정", "SubmenuLocations": "위치", @@ -81,7 +88,11 @@ "ThisUrlIsNotAValidGeoIPDB": "다운로드 한 파일은 유효한 GeoIP 데이터베이스가 아닙니다. URL을 다시 확인하거나 수동으로 파일을 다운로드하기 바랍니다.", "ToGeolocateOldVisits": "기존의 방문의 위치 데이터를 얻으려면 %1$s이 곳%2$s에 있는 스크립트 설명을 사용하세요.", "UnsupportedArchiveType": "지원되지 않는 압축 형식 %1$s 입니다.", + "UpdaterHasNotBeenRun": "업데이터가 한 번도 실행되지 않았습니다.", + "UpdaterIsNotScheduledToRun": "향후 실행할 계획이 없습니다.", + "UpdaterScheduledForNextRun": "다음 cron core:archive 명령 수행시 실행됩니다.", "UpdaterWasLastRun": "%s에 마지막 업데이트 확인", + "UpdaterWillRunNext": "%s에 실행이 계획되어 있습니다.", "WidgetLocation": "방문자 위치" } }
\ No newline at end of file diff --git a/plugins/UserCountry/lang/tr.json b/plugins/UserCountry/lang/tr.json index 58b4148923..16fa632c49 100644 --- a/plugins/UserCountry/lang/tr.json +++ b/plugins/UserCountry/lang/tr.json @@ -4,11 +4,13 @@ "Continent": "Kıta", "Country": "Ülke", "country_o1": "Diğer Ükle", + "DownloadingDb": "Karşıdan Yükleme %s", "Geolocation": "Coğrafi Lokasyon", "GeolocationPageDesc": "Bu sayfada Piwik'in ziyaretçi lokasyonunu nasıl bulacağını ayarlayabilirsiniz.", "Latitude": "Enlem", "Location": "Konum", "Longitude": "Boylam", + "Organization": "Organizasyon", "Region": "Bölge", "SubmenuLocations": "Konumlar" } diff --git a/plugins/UserCountryMap/lang/id.json b/plugins/UserCountryMap/lang/id.json index dc6cddf743..84fce004d8 100644 --- a/plugins/UserCountryMap/lang/id.json +++ b/plugins/UserCountryMap/lang/id.json @@ -1,5 +1,6 @@ { "UserCountryMap": { + "PluginDescription": "Pengaya ini menyediakan gawit Peta Pengunjung dan Peta Waktu-Nyata. Catatan: Membutuhkan pengaya NegaraPengguna diaktifkan.", "AndNOthers": "dan %s lain", "Cities": "Kita", "Countries": "Negara", @@ -17,6 +18,8 @@ "ShowingVisits": "Kunjungan Lokasi-Geo terakhir", "Unlocated": "<b>%s<\/b> %p kunjungan dari %c lokasi-geo tidak diketahui.", "VisitorMap": "Peta Pengunjung", - "WorldWide": "Seluruh Dunia" + "WorldWide": "Seluruh Dunia", + "WithUnknownRegion": "%s dengan wilayah tidak dikenal", + "WithUnknownCity": "%s dengan kota tidak dikenal" } }
\ No newline at end of file diff --git a/plugins/UserCountryMap/lang/ja.json b/plugins/UserCountryMap/lang/ja.json index ef083246b5..55de4296c1 100644 --- a/plugins/UserCountryMap/lang/ja.json +++ b/plugins/UserCountryMap/lang/ja.json @@ -1,5 +1,6 @@ { "UserCountryMap": { + "PluginDescription": "このプラグインでは、ビジターマップとリアルタイムマップウィジェットを提供します。注:利用可能なユーザーカントリープラグインが必要。", "AndNOthers": "と、%s ほか", "Cities": "都市", "Countries": "国", diff --git a/plugins/UserCountryMap/lang/ko.json b/plugins/UserCountryMap/lang/ko.json index e21ff537a5..d599797a57 100644 --- a/plugins/UserCountryMap/lang/ko.json +++ b/plugins/UserCountryMap/lang/ko.json @@ -1,5 +1,6 @@ { "UserCountryMap": { + "PluginDescription": "이 플러그인은 방문자 지도 및 실시간 지도 위젯을 제공합니다. 주의: UserCountry 플러그인이 활성화 되어 있어야 합니다.", "AndNOthers": ", 기타 %s", "Cities": "도시", "Countries": "국가", @@ -17,6 +18,8 @@ "ShowingVisits": "최근 방문의 지리적 위치", "Unlocated": "<b>%s<\/b> %c 방문에서 %p의 지리적 위치를 찾을 수 없습니다.", "VisitorMap": "방문자 지도", - "WorldWide": "전세계" + "WorldWide": "전세계", + "WithUnknownRegion": "%s 알 수 없는 지역", + "WithUnknownCity": "%s 알 수 없는 도시" } }
\ No newline at end of file diff --git a/plugins/UserLanguage/lang/id.json b/plugins/UserLanguage/lang/id.json index 7a9cc30d70..8d359ebf78 100644 --- a/plugins/UserLanguage/lang/id.json +++ b/plugins/UserLanguage/lang/id.json @@ -1,6 +1,7 @@ { "UserLanguage": { "BrowserLanguage": "Bahasa Peramban", - "LanguageCode": "Kode Bahasa" + "LanguageCode": "Kode Bahasa", + "PluginDescription": "Laporan bahasa yang digunakan oleh peramban pengguna" } }
\ No newline at end of file diff --git a/plugins/UserLanguage/lang/ko.json b/plugins/UserLanguage/lang/ko.json index 5cd9ae7a9f..9b50bdaf2a 100644 --- a/plugins/UserLanguage/lang/ko.json +++ b/plugins/UserLanguage/lang/ko.json @@ -1,6 +1,7 @@ { "UserLanguage": { "BrowserLanguage": "브라우저 언어", - "LanguageCode": "언어 코드" + "LanguageCode": "언어 코드", + "PluginDescription": "방문자의 브라우저를 통해 언어 기록" } }
\ No newline at end of file diff --git a/plugins/UsersManager/Controller.php b/plugins/UsersManager/Controller.php index 105d795036..37e13fcefa 100644 --- a/plugins/UsersManager/Controller.php +++ b/plugins/UsersManager/Controller.php @@ -9,6 +9,7 @@ namespace Piwik\Plugins\UsersManager; use Exception; +use Piwik\API\Request; use Piwik\API\ResponseBuilder; use Piwik\Common; use Piwik\Container\StaticContainer; @@ -19,7 +20,6 @@ use Piwik\Plugin\ControllerAdmin; use Piwik\Plugins\LanguagesManager\API as APILanguagesManager; use Piwik\Plugins\LanguagesManager\LanguagesManager; use Piwik\Plugins\Login\SessionInitializer; -use Piwik\Plugins\SitesManager\API as APISitesManager; use Piwik\Plugins\UsersManager\API as APIUsersManager; use Piwik\SettingsPiwik; use Piwik\Site; @@ -56,7 +56,7 @@ class Controller extends ControllerAdmin $view = new View('@UsersManager/index'); - $IdSitesAdmin = APISitesManager::getInstance()->getSitesIdWithAdminAccess(); + $IdSitesAdmin = Request::processRequest('SitesManager.getSitesIdWithAdminAccess'); $idSiteSelected = 1; if (count($IdSitesAdmin) > 0) { @@ -70,7 +70,7 @@ class Controller extends ControllerAdmin } else { $defaultReportSiteName = Site::getNameFor($idSiteSelected); try { - $usersAccessByWebsite = APIUsersManager::getInstance()->getUsersAccessFromSite($idSiteSelected); + $usersAccessByWebsite = Request::processRequest('UsersManager.getUsersAccessFromSite', array('idSite' => $idSiteSelected)); } catch (NoAccessException $e) { return $this->noAdminAccessToWebsite($idSiteSelected, $defaultReportSiteName, $e->getMessage()); } @@ -78,7 +78,7 @@ class Controller extends ControllerAdmin // we dont want to display the user currently logged so that the user can't change his settings from admin to view... $currentlyLogged = Piwik::getCurrentUserLogin(); - $usersLogin = APIUsersManager::getInstance()->getUsersLogin(); + $usersLogin = Request::processRequest('UsersManager.getUsersLogin'); foreach ($usersLogin as $login) { if (!isset($usersAccessByWebsite[$login])) { $usersAccessByWebsite[$login] = 'noaccess'; @@ -105,7 +105,7 @@ class Controller extends ControllerAdmin if (Piwik::isUserHasSomeAdminAccess()) { $view->showLastSeen = true; - $users = APIUsersManager::getInstance()->getUsers(); + $users = Request::processRequest('UsersManager.getUsers'); foreach ($users as $index => $user) { $usersAliasByLogin[$user['login']] = $user['alias']; @@ -131,7 +131,8 @@ class Controller extends ControllerAdmin $view->usersAliasByLogin = $usersAliasByLogin; $view->usersCount = count($users) - 1; $view->usersAccessByWebsite = $usersAccessByWebsite; - $websites = APISitesManager::getInstance()->getSitesWithAdminAccess(); + + $websites = Request::processRequest('SitesManager.getSitesWithAdminAccess'); uasort($websites, array('Piwik\Plugins\UsersManager\Controller', 'orderByName')); $view->websites = $websites; $this->setBasicVariablesView($view); @@ -228,7 +229,7 @@ class Controller extends ControllerAdmin $view = new View('@UsersManager/userSettings'); $userLogin = Piwik::getCurrentUserLogin(); - $user = APIUsersManager::getInstance()->getUser($userLogin); + $user = Request::processRequest('UsersManager.getUser', array('userLogin' => $userLogin)); $view->userAlias = $user['alias']; $view->userEmail = $user['email']; @@ -259,6 +260,7 @@ class Controller extends ControllerAdmin $view->languages = APILanguagesManager::getInstance()->getAvailableLanguageNames(); $view->currentLanguageCode = LanguagesManager::getLanguageCodeForCurrentUser(); + $view->currentTimeformat = LanguagesManager::uses12HourClockForCurrentUser(); $view->ignoreCookieSet = IgnoreCookie::isIgnoreCookieFound(); $view->piwikHost = Url::getCurrentHost(); $this->setBasicVariablesView($view); @@ -309,11 +311,13 @@ class Controller extends ControllerAdmin $userLogin = 'anonymous'; // Which websites are available to the anonymous users? - $anonymousSitesAccess = APIUsersManager::getInstance()->getSitesAccessFromUser($userLogin); + + $anonymousSitesAccess = Request::processRequest('UsersManager.getSitesAccessFromUser', array('userLogin' => $userLogin)); $anonymousSites = array(); foreach ($anonymousSitesAccess as $info) { $idSite = $info['site']; - $site = APISitesManager::getInstance()->getSiteFromId($idSite); + + $site = Request::processRequest('SitesManager.getSiteFromId', array('idSite' => $idSite)); // Work around manual website deletion if (!empty($site)) { $anonymousSites[$idSite] = $site; @@ -322,7 +326,7 @@ class Controller extends ControllerAdmin $view->anonymousSites = $anonymousSites; // Which report is displayed by default to the anonymous user? - $anonymousDefaultReport = APIUsersManager::getInstance()->getUserPreference($userLogin, APIUsersManager::PREFERENCE_DEFAULT_REPORT); + $anonymousDefaultReport = Request::processRequest('UsersManager.getUserPreference', array('userLogin' => $userLogin, 'preferenceName' => APIUsersManager::PREFERENCE_DEFAULT_REPORT)); if ($anonymousDefaultReport === false) { if (empty($anonymousSites)) { $anonymousDefaultReport = Piwik::getLoginPluginName(); @@ -379,12 +383,14 @@ class Controller extends ControllerAdmin $defaultReport = Common::getRequestVar('defaultReport'); $defaultDate = Common::getRequestVar('defaultDate'); $language = Common::getRequestVar('language'); + $timeFormat = Common::getRequestVar('timeformat'); $userLogin = Piwik::getCurrentUserLogin(); $this->processPasswordChange($userLogin); LanguagesManager::setLanguageForSession($language); APILanguagesManager::getInstance()->setLanguageForUser($userLogin, $language); + APILanguagesManager::getInstance()->set12HourClockForUser($userLogin, $timeFormat); APIUsersManager::getInstance()->setUserPreference($userLogin, APIUsersManager::PREFERENCE_DEFAULT_REPORT, diff --git a/plugins/UsersManager/javascripts/usersSettings.js b/plugins/UsersManager/javascripts/usersSettings.js index 809b1309b7..1708e64bf7 100644 --- a/plugins/UsersManager/javascripts/usersSettings.js +++ b/plugins/UsersManager/javascripts/usersSettings.js @@ -38,6 +38,7 @@ function sendUserSettingsAJAX() { postParams.defaultReport = defaultReport; postParams.defaultDate = defaultDate; postParams.language = $('#userSettingsTable #language').val(); + postParams.timeformat = $('#userSettingsTable #timeformat').val(); var ajaxHandler = new ajaxHelper(); ajaxHandler.addParams({ diff --git a/plugins/UsersManager/lang/cs.json b/plugins/UsersManager/lang/cs.json index 3fd20a14e2..80aab2ca4c 100644 --- a/plugins/UsersManager/lang/cs.json +++ b/plugins/UsersManager/lang/cs.json @@ -5,7 +5,7 @@ "AllWebsites": "Všechny weby", "AnonymousUser": "Anonymní uživatel", "AnonymousUserHasViewAccess": "Poznámka: %1$s uživatel má právo k přístupu k %2$s", - "AnonymousUserHasViewAccess2": "Vaše analytická hlášení a informace o návštěvnících je veřejně dostupná.", + "AnonymousUserHasViewAccess2": "Vaše analytická hlášení a informace o návštěvnících jsou veřejně dostupná.", "ApplyToAllWebsites": "Použít na všechny weby", "ChangeAllConfirm": "Jste si jistí, že chcete změnit '%s' oprávnění pro všechny weby?", "ChangePasswordConfirm": "Změna hesla také změní uživatelův token_auth. Opravdu chcete pokračovat?", @@ -15,8 +15,8 @@ "ConfirmProhibitMySuperUserAccess": "%s, opravdu chcete odstranit svůjvlastn í super uživatelský přístup? Přijdete o všechna oprávnění a o přístup k datům stránek a budete odhlášen z Piwiku.", "ConfirmProhibitOtherUsersSuperUserAccess": "Opravdu chcete odstranit super uživatelský přístup pro %s? Tento uživatel přijde o všechna práva a o přístup ke všem stránkám. Nezapomeňte mu povolit přístup k těm, které potřebuje, je-li to nutné.", "DeleteConfirm": "Jste si jistí, že chcete vymazat uživatele %s?", - "Email": "E-mail", - "EmailYourAdministrator": "%1$sPošlete administrátorovi e-mail o tomto problému%2$s.", + "Email": "Email", + "EmailYourAdministrator": "%1$sPošlete administrátorovi email o tomto problému%2$s.", "ExceptionAccessValues": "Parametr přístupu musí mít jednu z následujících hodnot: [ %s ]", "ExceptionAdminAnonymous": "Nemůžete dát 'admin' přístup 'anonymous' uživateli.", "ExceptionDeleteDoesNotExist": "Uživatel '%s' neexistuje a proto nemůže být vymazán.", @@ -61,7 +61,7 @@ "User": "Uživatel", "UsersManagement": "Správa uživatelů", "UsersManagementMainDescription": "Vytvořte nové uživatele, nebo aktualizujte existující. Níže můžete nastavit jejich oprávnění.", - "WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess": "Když uživatelé nejsou přihlášení do Piwiku, můžou mít přístup k", + "WhenUsersAreNotLoggedInAndVisitPiwikTheyShouldAccess": "Když uživatelé nejsou přihlášeni do Piwiku, mohou přistupovat k", "YourUsernameCannotBeChanged": "Vaše uživatelské jméno nemůže být změněno", "YourVisitsAreIgnoredOnDomain": "%sVaše návštěvy jsou vynechávány Piwikem na %s %s (cookie pro vynechání byla nalezena ve vašem prohlížeči).", "YourVisitsAreNotIgnored": "%sVaše návštěvy nejsou vynechávány Piwikem%s (cookie pro vynechání nebyla nalezena ve vašem prohlížeči)." diff --git a/plugins/UsersManager/lang/ja.json b/plugins/UsersManager/lang/ja.json index 6f1b01a556..2d1bf2acb5 100644 --- a/plugins/UsersManager/lang/ja.json +++ b/plugins/UsersManager/lang/ja.json @@ -42,8 +42,11 @@ "MenuAnonymousUserSettings": "anonymous ユーザーの設定", "MenuUsers": "ユーザー", "MenuUserSettings": "ユーザーの設定", + "MenuPersonal": "個人用", + "PersonalSettings": "個人設定", "NoteNoAnonymousUserAccessSettingsWontBeUsed2": "注) 匿名ユーザーがアクセスできるウェブサイトをお持ちでないため、このセクションでは設定の変更ができません。", "NoUsersExist": "まだユーザーがいません。", + "PluginDescription": "ユーザー管理では、新しいユーザーの追加、既存ユーザーの編集、および web サイトの管理や閲覧の権限を設定できます。", "PrivAdmin": "管理", "PrivNone": "権限なし", "PrivView": "ビュー", diff --git a/plugins/UsersManager/templates/index.twig b/plugins/UsersManager/templates/index.twig index e2fb24dc9a..6bf12d01e0 100644 --- a/plugins/UsersManager/templates/index.twig +++ b/plugins/UsersManager/templates/index.twig @@ -15,6 +15,7 @@ {% endset %} <div piwik-siteselector + show-selected-site="true" class="sites_autocomplete" siteid="{{ idSiteSelected }}" sitename="{{ defaultReportSiteName }}" diff --git a/plugins/UsersManager/templates/userSettings.twig b/plugins/UsersManager/templates/userSettings.twig index 57e04dff85..6b075eda86 100644 --- a/plugins/UsersManager/templates/userSettings.twig +++ b/plugins/UsersManager/templates/userSettings.twig @@ -45,6 +45,14 @@ </div> <div class="form-group"> + <label for="timeformat">{{ 'General_TimeFormat'|translate }}</label> + <select name="timeformat" id="timeformat"> + <option value="1" {% if currentTimeformat == 1 %}selected="selected"{% endif %} title="{{ 'General_12HourClock'|translate }}">{{ 'General_12HourClock'|translate }}</option> + <option value="0" {% if currentTimeformat == 0 %}selected="selected"{% endif %} title="{{ 'General_24HourClock'|translate }}">{{ 'General_24HourClock'|translate }}</option> + </select> + </div> + + <div class="form-group"> <label>{{ 'UsersManager_ReportToLoadByDefault'|translate }}</label> <label class="radio"> <input id="defaultReportRadioAll" type="radio" value="MultiSites" @@ -57,6 +65,7 @@ {{ 'General_DashboardForASpecificWebsite'|translate }} </label> <div piwik-siteselector + show-selected-site="true" class="sites_autocomplete" siteid="{{ defaultReportIdSite }}" sitename="{{ defaultReportSiteName }}" diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php index a6f9971480..550212d9e3 100644 --- a/plugins/VisitFrequency/API.php +++ b/plugins/VisitFrequency/API.php @@ -45,7 +45,6 @@ class API extends \Piwik\Plugin\API 'segment' => $segment, 'columns' => implode(',', $columns), 'format' => 'original', - 'serialize' => 0, // tests set this to 1 'format_metrics' => 0 ); diff --git a/plugins/VisitFrequency/lang/id.json b/plugins/VisitFrequency/lang/id.json index 4149ae29fc..d44b555ef2 100644 --- a/plugins/VisitFrequency/lang/id.json +++ b/plugins/VisitFrequency/lang/id.json @@ -10,6 +10,8 @@ "ColumnReturningVisits": "Kunjungan Kembali", "ColumnSumVisitLengthReturning": "Jumlah waktu digunakan oleh pengunjung kembali (dalam detik)", "ColumnUniqueReturningVisitors": "Pengnjung kembali unik", + "ColumnReturningUsers": "Penguna kembali", + "PluginDescription": "Laporan matriks mengenai pengunjung baru dan pengunjung kembali.", "ReturnActions": "%s tindakan tiap kunjungan kembali", "ReturnAverageVisitDuration": "%s rerata waktu kunjungan pengunjung kembali", "ReturnAvgActions": "%s tindakan tiap kunjungan kembali", diff --git a/plugins/VisitFrequency/lang/ko.json b/plugins/VisitFrequency/lang/ko.json index 4192687557..7a5880db1c 100644 --- a/plugins/VisitFrequency/lang/ko.json +++ b/plugins/VisitFrequency/lang/ko.json @@ -10,6 +10,8 @@ "ColumnReturningVisits": "돌아온 방문 수", "ColumnSumVisitLengthReturning": "리피터의 총 머문 시간 (초)", "ColumnUniqueReturningVisitors": "고유한 돌아온 방문자", + "ColumnReturningUsers": "돌아온 사용자", + "PluginDescription": "처음 방문한 방문자와 다시 돌아온 방문자에 대한", "ReturnActions": "%s 돌아온 방문의 활동", "ReturnAverageVisitDuration": "%s 돌아온 방문자의 평균 방문 시간", "ReturnAvgActions": "%s 돌아온 방문당 활동", @@ -17,8 +19,8 @@ "ReturningVisitDocumentation": "돌아온 방문은 (신규 방문과는 대조적으로), 적어도 한 번은 이전에 웹 사이트를 방문한 사람에 의해 이루어집니다.", "ReturningVisitsDocumentation": "돌아온 방문 개요입니다.", "ReturnVisits": "%s 돌아온 방문", - "SubmenuFrequency": "프리퀸시", + "SubmenuFrequency": "빈도", "WidgetGraphReturning": "돌아온 방문 수 그래프", - "WidgetOverview": "프리퀸시 개요" + "WidgetOverview": "빈도 개요" } }
\ No newline at end of file diff --git a/plugins/VisitTime/lang/cs.json b/plugins/VisitTime/lang/cs.json index ab1e8150f7..c49f2bc6a6 100644 --- a/plugins/VisitTime/lang/cs.json +++ b/plugins/VisitTime/lang/cs.json @@ -6,13 +6,13 @@ "LocalTime": "Návštěvy podle lokálního času", "NHour": "%sh", "PluginDescription": "Hlásí místní a serverový čas, kdy vaši návštěvníci zobrazují stránky nebo aplikaci.", - "ServerTime": "Návštěvy podle serverového času", + "ServerTime": "Návštěvy podle času na serveru", "SubmenuTimes": "Časy", "VisitsByDayOfWeek": "Návštěvy podle dnů v týdnu", "WidgetByDayOfWeekDocumentation": "Tento graf zobrazuje, kolik návštěv obdržela vaše stránka každý den v týdnu.", "WidgetLocalTime": "Návštěvy podle lokálního času", "WidgetLocalTimeDocumentation": "Tento graf ukazuje čas v %s návštěvníkovo časové zóně %s během jěho návštěvy.", - "WidgetServerTime": "Návštěvy podle serverového času", + "WidgetServerTime": "Návštěvy podle času na serveru", "WidgetServerTimeDocumentation": "Tento graf ukazuje jaký čas byl na %s serveru časové zóny %s během návstěvy." } }
\ No newline at end of file diff --git a/plugins/VisitTime/lang/ko.json b/plugins/VisitTime/lang/ko.json index c83bd88aac..216933c951 100644 --- a/plugins/VisitTime/lang/ko.json +++ b/plugins/VisitTime/lang/ko.json @@ -5,6 +5,7 @@ "DayOfWeek": "요일", "LocalTime": "현지 시간 기준 방문 수", "NHour": "%s시", + "PluginDescription": "방문자가 웹사이트나 앱을 볼 때 현지 시간 및 서버 시간을 알려줍니다.", "ServerTime": "서버 시간 기준 방문 수", "SubmenuTimes": "방문시간", "VisitsByDayOfWeek": "요일별 방문수", diff --git a/plugins/VisitorGenerator b/plugins/VisitorGenerator -Subproject 6ef251bbe316882c010408180c293af3b2a2ef2 +Subproject c3abd090cf8a89894ce427b56312920f794e612 diff --git a/plugins/VisitorInterest/lang/cs.json b/plugins/VisitorInterest/lang/cs.json index 151191809e..6ee78b7b7e 100644 --- a/plugins/VisitorInterest/lang/cs.json +++ b/plugins/VisitorInterest/lang/cs.json @@ -8,17 +8,17 @@ "NPages": "%s stránek", "OnePage": "1 stránka", "PluginDescription": "Hlásí zájem vašich návštěvníků: počet zobrazených stránek, čas na stránkách, dny od poslední návštěvy aj.", - "VisitNum": "Číslo návstěvníka", - "VisitsByDaysSinceLast": "Návstěv po dnech od poslední návštěvy", + "VisitNum": "Číslo návštěvníka", + "VisitsByDaysSinceLast": "Návštěvy po dnech od posledního navštívení", "visitsByVisitCount": "Návstěvy podle pořadí", - "VisitsPerDuration": "Návštěv za dobu návštěvy", - "VisitsPerNbOfPages": "Návštěv za počet stránek", + "VisitsPerDuration": "Doba zobrazení na návštěvníka", + "VisitsPerNbOfPages": "Počet stránek na návštěvníka", "WidgetLengths": "Doba návštěv", "WidgetLengthsDocumentation": "V tomto hlášení vidíte, kolik návštěv mělo určitou celkovou délku. Nejdříve je toto hlášení zobrazeno jako tag cloud, častější doby jsou větším písmem.", "WidgetPages": "Stránek za návštěvu", "WidgetPagesDocumentation": "V tomto hlášení je zobrazeno, kolik návštěv provedlo určité množství zobrazení stránek. Nejdříve je toto hlášení zobrazeno jako tag cloud, častější čísla mají větší písmo.", "WidgetVisitsByDaysSinceLast": "Návštěvy podle dnů od poslední návštěvy", - "WidgetVisitsByDaysSinceLastDocumentation": "V hlášení můžete vidět kolik návstěv bylo od návstěvníků, kteří již v minulosti po několika dnech stránku navstívili.", + "WidgetVisitsByDaysSinceLastDocumentation": "V tomto hlášení vidíte kolik návštěv provedli návštěvníci, kteří v minulosti stránku navštívili opakovaně.", "WidgetVisitsByNumDocumentation": "V tomto hlášení vidíte, kolik návštěv bylo n-tou návštěvou, t. j. návštěvníky, kteří navštívili vaše stránky aspoň n-krát." } }
\ No newline at end of file diff --git a/plugins/VisitorInterest/lang/ja.json b/plugins/VisitorInterest/lang/ja.json index fab4023ba2..521c594e90 100644 --- a/plugins/VisitorInterest/lang/ja.json +++ b/plugins/VisitorInterest/lang/ja.json @@ -7,6 +7,7 @@ "Engagement": "利用状況", "NPages": "%s ページ", "OnePage": "1 ページ", + "PluginDescription": "ビジターの関心についてのレポート:閲覧したページ数、ウェブサイトに費やされた時間、最後の訪問以来の日数、その他", "VisitNum": "訪問回数", "VisitsByDaysSinceLast": "最後の訪問からの日数別のビジット", "visitsByVisitCount": "訪問回数別のビジット", diff --git a/plugins/VisitorInterest/lang/ko.json b/plugins/VisitorInterest/lang/ko.json index be803fec11..75b9e8c7ce 100644 --- a/plugins/VisitorInterest/lang/ko.json +++ b/plugins/VisitorInterest/lang/ko.json @@ -7,6 +7,7 @@ "Engagement": "이용상황", "NPages": "%s페이지", "OnePage": "1페이지", + "PluginDescription": "방문자의 관심에 대한 보고서: 읽은 페이지 수, 웹사이트 방문 시간, 지난 마지막 방문으로부터 며칠 등", "VisitNum": "방문 횟수", "VisitsByDaysSinceLast": "마지막 방문일로부터 방문", "visitsByVisitCount": "방문 빈도", diff --git a/plugins/VisitsSummary/lang/cs.json b/plugins/VisitsSummary/lang/cs.json index 54a409e23d..004245c9e4 100644 --- a/plugins/VisitsSummary/lang/cs.json +++ b/plugins/VisitsSummary/lang/cs.json @@ -6,7 +6,7 @@ "GenerateTime": "%s sekund k vygenerování stránky", "MaxNbActions": "%s maximální počet akcí na návštěvu", "NbActionsDescription": "%s akcí (zobrazení stránek, stažení a odchodů)", - "NbActionsPerVisit": "%s akcí za návštěvu", + "NbActionsPerVisit": "%s akcí (zobrazení stránek, stažení, odkazů a vyhledávání) na návštěvu", "NbDownloadsDescription": "%s stažení", "NbKeywordsDescription": "%s unikátní klíčová slova", "NbOutlinksDescription": "%s externích odkazů", diff --git a/plugins/VisitsSummary/lang/ko.json b/plugins/VisitsSummary/lang/ko.json index c7b22f205b..ec21c7d19e 100644 --- a/plugins/VisitsSummary/lang/ko.json +++ b/plugins/VisitsSummary/lang/ko.json @@ -1,5 +1,6 @@ { "VisitsSummary": { + "AverageGenerationTime": "%s 평균 페이지 생성 시간", "AverageVisitDuration": "%s 방문의 평균 지속 시간", "GenerateQueries": "%s 쿼리 실행됨", "GenerateTime": "페이지 생성에 %s 초 걸림", @@ -16,6 +17,7 @@ "NbUniquePageviewsDescription": "%s 고유 페이지뷰", "NbUniqueVisitors": "%s 고유 방문자", "NbVisitsBounced": "%s 반송률 (첫 페이지에서 이탈)", + "PluginDescription": "보고서 기본 분석 측정 기준: 방문횟수, 고유 방문자, 활동 횟수, 반송률 등", "VisitsSummary": "방문 개요", "VisitsSummaryDocumentation": "방문 추이 개요입니다.", "WidgetLastVisits": "최근 방문 그래프", diff --git a/plugins/WebsiteMeasurable/lang/ja.json b/plugins/WebsiteMeasurable/lang/ja.json new file mode 100644 index 0000000000..51f760f6ac --- /dev/null +++ b/plugins/WebsiteMeasurable/lang/ja.json @@ -0,0 +1,7 @@ +{ + "WebsiteMeasurable": { + "Website": "ウェブサイト", + "Websites": "ウェブサイト", + "WebsiteDescription": "Web サイトは、通常単一の web ドメインから提供される web ページで構成されています。" + } +}
\ No newline at end of file diff --git a/plugins/WebsiteMeasurable/lang/ko.json b/plugins/WebsiteMeasurable/lang/ko.json new file mode 100644 index 0000000000..769d1388fe --- /dev/null +++ b/plugins/WebsiteMeasurable/lang/ko.json @@ -0,0 +1,7 @@ +{ + "WebsiteMeasurable": { + "Website": "웹사이트", + "Websites": "웹사이트", + "WebsiteDescription": "웹 페이지가 포함된 웹사이트는 대체로 하나의 웹 도메인에서 제공됩니다." + } +}
\ No newline at end of file diff --git a/plugins/Widgetize/lang/nb.json b/plugins/Widgetize/lang/nb.json index a448e5258f..659ed7e34f 100644 --- a/plugins/Widgetize/lang/nb.json +++ b/plugins/Widgetize/lang/nb.json @@ -1,5 +1,6 @@ { "Widgetize": { - "OpenInNewWindow": "Åpne i nytt vindu" + "OpenInNewWindow": "Åpne i nytt vindu", + "TopLinkTooltip": "Eksporter Piwik-rapporter som widgeter og bygg dem inn kontrollpanelet i din app som en iframe." } }
\ No newline at end of file |