Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display labels correctly in Excel / LibreOffice #10014

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 11 additions & 4 deletions CHANGELOG.md
Expand Up @@ -6,14 +6,21 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API'

### New features
* New method `setIsWritableByCurrentUser` for `SystemSetting` to change the writable permission for certain system settings via DI.
* JS Tracker: `setDomains` function now supports page wildcards matching eg. `example.com/index*` which can be useful when [tracking a group of pages within a domain in a separate website in Piwik](http://developer.piwik.org/guides/tracking-javascript-guide#tracking-a-group-of-pages-in-a-separate-website)
* To customise the list of URL query parameters to be removed from your URLs, you can now define and overwrite `url_query_parameter_to_exclude_from_url` INI setting in your `config.ini.php` file. By default, the following query string parameters will be removed: `gclid, fb_xd_fragment, fb_comment_id, phpsessid, jsessionid, sessionid, aspsessionid, doing_wp_cron, sid`.

### Breaking Changes
### Deprecations
### New APIs
### New commands
* The following PHP functions have been deprecated and will be removed in Piwik 3.0:
* `SettingsServer::isApache()`

### New guides
### Library updates
* JavaScript Tracker: [Measuring domains and/or sub-domains](http://developer.piwik.org/guides/tracking-javascript-guide#measuring-domains-andor-sub-domains)

### Breaking Changes
* Reporting API: when a cell value in a CSV or TSV (excel) data export starts with a character `=`, `-` or `+`, Piwik will now prefix the value with `'` to ensure that it is displayed correctly in Excel or OpenOffice/LibreOffice.

### Internal change
* Tracking API: by default, when tracking a Page URL, Piwik will now remove the URL query string parameter `sid` if it is found.
* In the JavaScript tracker, the function `setDomains` will not anymore attempt to set a cookie path. Learn more about [configuring the tracker correctly](http://developer.piwik.org/guides/tracking-javascript-guide#tracking-one-domain) when tracking one or several domains and/or paths.

## Piwik 2.16.0
Expand Down
12 changes: 7 additions & 5 deletions core/Archive.php
Expand Up @@ -646,7 +646,7 @@ private function getArchiveIds($archiveNames)
$doneFlags = array();
$archiveGroups = array();
foreach ($plugins as $plugin) {
$doneFlag = $this->getDoneStringForPlugin($plugin);
$doneFlag = $this->getDoneStringForPlugin($plugin, $this->params->getIdSites());

$doneFlags[$doneFlag] = true;
if (!isset($this->idarchives[$doneFlag])) {
Expand Down Expand Up @@ -731,7 +731,7 @@ private function cacheArchiveIdsWithoutLaunching($plugins)

// initialize archive ID cache for each report
foreach ($plugins as $plugin) {
$doneFlag = $this->getDoneStringForPlugin($plugin);
$doneFlag = $this->getDoneStringForPlugin($plugin, $this->params->getIdSites());
$this->initializeArchiveIdCache($doneFlag);
}

Expand All @@ -749,10 +749,10 @@ private function cacheArchiveIdsWithoutLaunching($plugins)
* @param string $plugin
* @return string
*/
private function getDoneStringForPlugin($plugin)
private function getDoneStringForPlugin($plugin, $idSites)
{
return Rules::getDoneStringFlagFor(
$this->params->getIdSites(),
$idSites,
$this->params->getSegment(),
$this->getPeriodLabel(),
$plugin
Expand Down Expand Up @@ -893,9 +893,11 @@ private function prepareArchive(array $archiveGroups, Site $site, Period $period

$periodString = $period->getRangeString();

$idSites = array($site->getId());

// process for each plugin as well
foreach ($archiveGroups as $plugin) {
$doneFlag = $this->getDoneStringForPlugin($plugin);
$doneFlag = $this->getDoneStringForPlugin($plugin, $idSites);
$this->initializeArchiveIdCache($doneFlag);

$idArchive = $archiveLoader->prepareArchive($plugin);
Expand Down
20 changes: 11 additions & 9 deletions core/ArchiveProcessor/Rules.php
Expand Up @@ -172,20 +172,21 @@ public static function isArchivingDisabledFor(array $idSites, Segment $segment,
|| $generalConfig['archiving_range_force_on_browser_request'] != false
) {
return false;
} else {
Log::debug("Not forcing archiving for range period.");
}

Log::debug("Not forcing archiving for range period.");
$processOneReportOnly = false;

} else {
$processOneReportOnly = !self::shouldProcessReportsAllPlugins($idSites, $segment, $periodLabel);
}

$processOneReportOnly = !self::shouldProcessReportsAllPlugins($idSites, $segment, $periodLabel);
$isArchivingDisabled = !self::isRequestAuthorizedToArchive() || self::$archivingDisabledByTests;
$isArchivingEnabled = self::isRequestAuthorizedToArchive() && !self::$archivingDisabledByTests;

if ($processOneReportOnly
&& $periodLabel != 'range'
) {
if ($processOneReportOnly) {
// When there is a segment, we disable archiving when browser_archiving_disabled_enforce applies
if (!$segment->isEmpty()
&& $isArchivingDisabled
&& !$isArchivingEnabled
&& $generalConfig['browser_archiving_disabled_enforce']
&& !SettingsServer::isArchivePhpTriggered() // Only applies when we are not running core:archive command
) {
Expand All @@ -196,7 +197,8 @@ public static function isArchivingDisabledFor(array $idSites, Segment $segment,
// Always allow processing one report
return false;
}
return $isArchivingDisabled;

return !$isArchivingEnabled;
}

public static function isRequestAuthorizedToArchive()
Expand Down
27 changes: 26 additions & 1 deletion core/DataTable/Renderer/Csv.php
Expand Up @@ -121,7 +121,6 @@ protected function renderTable($table, &$allColumns = array())
{
if (is_array($table)) {
// convert array to DataTable

$table = DataTable::makeFromSimpleArray($table);
}

Expand Down Expand Up @@ -247,6 +246,9 @@ protected function formatValue($value)
} elseif ($value === false) {
$value = 0;
}

$value = $this->formatFormulas($value);

if (is_string($value)
&& (strpos($value, '"') !== false
|| strpos($value, $this->separator) !== false)
Expand All @@ -264,6 +266,29 @@ protected function formatValue($value)
return $value;
}

protected function formatFormulas($value)
{
$formulaStartsWith = array('=', '+', '-');

// remove first % sign
$count = 1;
$valueWithoutPercentSign = str_replace('%', '', $value, $count);

if (empty($valueWithoutPercentSign)
|| !is_string($value)
|| is_numeric($valueWithoutPercentSign)) {
return $value;
}

$firstCharCellValue = $valueWithoutPercentSign[0];
$isFormula = in_array($firstCharCellValue, $formulaStartsWith);
if($isFormula) {
return "'" . $value;
}

return $value;
}

/**
* Sends the http headers for csv file
*/
Expand Down
2 changes: 1 addition & 1 deletion core/Version.php
Expand Up @@ -20,7 +20,7 @@ final class Version
* The current Piwik version.
* @var string
*/
const VERSION = '2.16.1-b3';
const VERSION = '2.16.1-rc1';

public function isStableVersion($version)
{
Expand Down
1 change: 1 addition & 0 deletions lang/pl.json
Expand Up @@ -13,6 +13,7 @@
"AllWebsitesDashboard": "Tablica analiz wszystkich stron",
"And": "i",
"API": "API",
"Apply": "Zastosuj",
"ArchivingInlineHelp": "Dla serwisów o średnim i wysokim natężeniu ruchu, zalecane jest wyłączenie archiwizacji danych Piwik podczas przeglądania raportów w przeglądarce. Zamiast tego zaleca się skonfigurowanie żądania cron, by przetwarzanie raportów Piwik następowało co godzinę.",
"ArchivingTriggerDescription": "Zalecany dla większych instalacji Piwik, musisz %1$sskonfigurować crona%2$s do przetwarzania raportów automatycznie.",
"AuthenticationMethodSmtp": "Metoda uwierzytelnienie dla SMTP",
Expand Down
2 changes: 1 addition & 1 deletion lang/sr.json
Expand Up @@ -351,7 +351,7 @@
"TotalVisitsPageviewsActionsRevenue": "(Ukupno poseta %1$s, prikaza %2$s, akcija %3$s, zarada %4$s)",
"TransitionsRowActionTooltip": "Pogledajte šta su posetioci radili pre i posle posete ovoj stranici",
"TransitionsRowActionTooltipTitle": "Otvori tranzicije",
"TranslatorName": "Petar Benke, Branislav Maksin, Nikola Stojković",
"TranslatorName": "<a href=\"https:\/\/www.linkedin.com\/in\/petar-benke-905a02b8\">Petar Benke<\/a>, Branislav Maksin, Nikola Stojković",
"UniquePurchases": "Jedinstvene porudžbine",
"Unknown": "Nepoznato",
"Upload": "Okači",
Expand Down
56 changes: 56 additions & 0 deletions plugins/API/tests/Unit/CsvRendererTest.php
Expand Up @@ -105,6 +105,62 @@ public function test_renderScalar_shouldNotRemoveLineBreaks()
The\nOutput', $response);
}

/**
* @dataProvider getCellValuesToPrefixOrNot
*/
public function test_renderScalar_whenCellValueIsFormula_shouldPrefixWithQuote($value, $expectedCsvValue)
{
$response = $this->builder->renderScalar($value);

$this->assertEquals("value\n$expectedCsvValue", $response);
}

public function getCellValuesToPrefixOrNot()
{
// input, expected csv output
return array(
// we prefix with quotes
array('=test()', '\'=test()'),
array('=1+1', '\'=1+1'),
array('+1+1', '\'+1+1'),
array('+1+test()', '\'+1+test()'),
array('-1+1', '\'-1+1'),
array('-test()', '\'-test()'),
array('-te,st()', '"\'-te,st()"'),
array('-te"st()', '"\'-te""st()"'),

// we do not need to prefix with quote
array('1', '1'),
array('2', '2'),
array('20000000', '20000000'),
array('10%', '10%'),
array('10.5%', '10.5%'),
array('-10.5%', '-10.5%'),
array('+10,5%', '"\'+10,5%"'),
array('10,5', '"10,5"'),
array('1+test()', '1+test()'),
array('', ''),
array(0, '0'),
array(2.2, '2.2'),
array(-2.2, '-2.2'),
array(false, '0'),
array(null, ''),
);
}

/**
* @dataProvider getCellValuesToPrefixOrNot
*/
public function test_renderDataTable_shouldRenderFormulas($value, $expectedValue)
{
$dataTable = new DataTable();
$dataTable->addRowFromSimpleArray(array('nb_visits' => 5, 'nb_random' => $value));

$response = $this->builder->renderDataTable($dataTable);

$this->assertEquals("nb_visits,nb_random\n5,$expectedValue", $response);
}

public function test_renderDataTable_shouldRenderABasicDataTable()
{
$dataTable = new DataTable();
Expand Down
1 change: 1 addition & 0 deletions plugins/CoreAdminHome/lang/ru.json
Expand Up @@ -88,6 +88,7 @@
"YouAreOptedOut": "Вы отказались от сбора статистических данных.",
"YouMayOptOut": "Вы можете отказаться от уникального cookie, привязанному к вашему браузеру, и идентифицирующего вас в системе аналитики данного сайта, тем самым отказавшись от любого сбора сведений о вас и их анализа на данном сайте.",
"YouMayOptOutBis": "Снимите галочку и получите исключающий cookie для отказа сбора данных.",
"OptingYouOut": "Отписываем вас, пожалуйста, подождите...",
"ProtocolNotDetectedCorrectly": "В данный момент вы просматриваете Piwik через безопасное соединение SSL (используя https), но Piwic смог обнаружить только небезопасное соединение на сервере.",
"ProtocolNotDetectedCorrectlySolution": "Чтобы удостовериться, что Piwic безопасно запрашивает и обрабатывает ваш контент через HTTPS, вы можете отредактировать свой файл %1$s или настроить ваши настройки прокси, или вы можете добавить линию %2$s под разделом %3$s. %4$sУзнать больше%5$s"
}
Expand Down
8 changes: 7 additions & 1 deletion plugins/CoreHome/lang/ru.json
Expand Up @@ -24,6 +24,7 @@
"InjectedHostSuperUserWarning": "Piwik может быть неправильно сконфигурирован (например, если Piwik был недавно перемещен на новый сервер или переехал на новый адрес). Вы также можете %1$sнажать сюда и добавить %2$s в качестве доверенного имени хоста Piwik (если вы доверяете ему)%3$s, или %4$sнажмите сюда и перейдите %5$s для безопасного доступа к Piwik%6$s.",
"InjectedHostWarningIntro": "Сейчас вы заходите в Piwik на %1$s, но Piwik был настроен на этом адресе: %2$s.",
"JavascriptDisabled": "JavaScript должен быть разрешен для корректного стандартного отображения Piwik.<br \/>Если Вы читаете это сообщение, значит JavaScript запрещен, либо не поддерживается вашим браузером.<br \/>Для использования стандартного вида, разрешите JavaScript в настройках вашего браузера, потом %1$sпопробуйте обновить страницу%2$s.<br \/>",
"MainNavigation": "Основная навигация",
"MakeADifference": "Внесите свой вклад: %1$sПожертвуйте сейчас%2$s для финансирования Piwik 2.0!",
"MakeOneTimeDonation": "Сделать только пожертвование (без подписки).",
"Menu": "Меню",
Expand All @@ -46,6 +47,11 @@
"ViewAllPiwikVideoTutorials": "Просмотреть все обучающие ролики про Piwik",
"WebAnalyticsReports": "Отчеты веб аналитики",
"YouAreUsingTheLatestVersion": "У вас последняя версия Piwik!",
"ClickRowToExpandOrContract": "Нажмите на эту строку, чтобы растянуть или сжать подтаблицу."
"ClickRowToExpandOrContract": "Нажмите на эту строку, чтобы растянуть или сжать подтаблицу.",
"QuickAccessTitle": "Поиск %s. Используйте клавиши со стрелками для переходов в пределах результатов поиска. Быстрый доступ: нажмите 'f' для поиска.",
"MenuEntries": "Пункты меню",
"Segments": "Сегменты",
"AdblockIsMaybeUsed": "В случае, если вы используете блокировщик рекламы, пожалуйста, отключите его, чтобы убедиться, что Piwik работает без проблем.",
"ChangeCurrentWebsite": "Выбор вебсайта, сейчас выбран вебсайт: %s"
}
}
2 changes: 1 addition & 1 deletion plugins/CoreHome/templates/_dataTableCell.twig
Expand Up @@ -47,7 +47,7 @@
{% if row.getMetadata('html_label_prefix') %}<span class='label-prefix'>{{ row.getMetadata('html_label_prefix') | raw }}&nbsp;</span>{% endif -%}
{%- if row.getMetadata('html_label_suffix') %}<span class='label-suffix'>{{ row.getMetadata('html_label_suffix') | raw }}</span>{% endif -%}
{% endif %}<span class="value">
{%- if row.getColumn(column) %}{% if column=='label' %}{{- row.getColumn(column)|rawSafeDecoded -}}{% else %}{{- row.getColumn(column)|number(2,0)|raw -}}{% endif %}
{%- if row.getColumn(column) or (column=='label' and row.getColumn(column) is same as("0")) %}{% if column=='label' %}{{- row.getColumn(column)|rawSafeDecoded -}}{% else %}{{- row.getColumn(column)|number(2,0)|raw -}}{% endif %}
{%- else -%}-
{%- endif -%}</span>
{% if column=='label' %}</span>{% endif %}
Expand Down