';
+ }
+ var urlLinkDom = $('.urlLink',this);
+ var urlToLink = $(urlLinkDom).html();
+ $(urlLinkDom).remove();
+
+ var truncationOffsetBecauseImageIsPrepend = -2; //website subtable needs -9.
+
+ self.truncate( $(this), truncationOffsetBecauseImageIsPrepend );
+ if( urlToLink.match("javascript:") )
+ {
+ $(this).prepend(imgToPrepend).wrapInner('');
+ }
+ else
+ {
+ $(this).prepend(imgToPrepend).wrapInner('');
+ }
+ });
+ }
+ },
+
+ // if sorting the columns is enabled, when clicking on a column,
+ // - if this column was already the one used for sorting, we revert the order desc<->asc
+ // - we send the ajax request with the new sorting information
+ handleSort: function(domElem)
+ {
+ var self = this;
+ if( self.param.enable_sort )
+ {
+ $('.sortable', domElem).click(
+ function()
+ {
+ $(this).unbind('click');
+ self.onClickSort(this);
+ }
+ );
+
+ // are we in a subdatatable?
+ var currentIsSubDataTable = $(domElem).parent().hasClass('cellSubDataTable');
+
+ var prefixSortIcon = '';
+ if(currentIsSubDataTable)
+ {
+ prefixSortIcon = '_subtable_';
+ }
+ var imageSortWidth = 16;
+ var imageSortHeight = 16;
+ // we change the style of the column currently used as sort column
+ // adding an image and the class columnSorted to the TD
+ $(".sortable#"+self.param.filter_sort_column+' #thDIV', domElem).parent()
+ .addClass('columnSorted')
+ .prepend('
\
+ ')
+ .click( function() {
+ $('#keyword', target).val('');
+ $(':submit', target).submit();
+ });
+ $('#keyword',this).after(clearImg);
+
+ }
+ }
+ );
+ },
+
+ //behaviour for '< prev' 'next >' links and page count
+ handleOffsetInformation: function(domElem)
+ {
+ var self = this;
+
+ $('.dataTablePages', domElem).each(
+ function(){
+ var offset = 1+Number(self.param.filter_offset);
+ var offsetEnd = Number(self.param.filter_offset) + Number(self.param.filter_limit);
+ var totalRows = Number(self.param.totalRows);
+ offsetEndDisp = offsetEnd;
+
+ if(offsetEnd > totalRows) offsetEndDisp = totalRows;
+
+ // only show this string if there is some rows in the datatable
+ if(totalRows != 0)
+ {
+ var str = sprintf(_pk_translate('CoreHome_PageOf_js'),offset + '-' + offsetEndDisp,totalRows);
+ $(this).text(str);
+ }
+ }
+ );
+
+ // Display the next link if the total Rows is greater than the current end row
+ $('.dataTableNext', domElem)
+ .each(function(){
+ var offsetEnd = Number(self.param.filter_offset)
+ + Number(self.param.filter_limit);
+ var totalRows = Number(self.param.totalRows);
+ if(offsetEnd < totalRows)
+ {
+ $(this).css('display','inline');
+ }
+ })
+ // bind the click event to trigger the ajax request with the new offset
+ .click(function(){
+ $(this).unbind('click');
+ self.param.filter_offset = Number(self.param.filter_offset) + Number(self.param.filter_limit);
+ self.reloadAjaxDataTable();
+ })
+ ;
+
+ // Display the previous link if the current offset is not zero
+ $('.dataTablePrevious', domElem)
+ .each(function(){
+ var offset = 1+Number(self.param.filter_offset);
+ if(offset != 1)
+ {
+ $(this).css('display','inline');
+ }
+ }
+ )
+ // bind the click event to trigger the ajax request with the new offset
+ // take care of the negative offset, we setup 0
+ .click(
+ function(){
+ $(this).unbind('click');
+ var offset = Number(self.param.filter_offset) - Number(self.param.filter_limit);
+ if(offset < 0) { offset = 0; }
+ self.param.filter_offset = offset;
+ self.reloadAjaxDataTable();
+ }
+ );
+ },
+
+ // DataTable view box (data, table, cloud, graph, ...)
+ handleExportBox: function(domElem)
+ {
+ var self = this;
+ if( self.param.idSubtable )
+ {
+ // no view box for subtables
+ return;
+ }
+
+ // When the (+) image is hovered, the export buttons are displayed
+ $('.dataTableFooterIconsShow', domElem)
+ .show()
+ .hover( function() {
+ $(this).fadeOut('slow');
+ $('.exportToFormatIcons', $(this).parent()).show('slow');
+ }, function(){}
+ );
+
+ //timeout object used to hide the datatable export buttons
+ var timeout = null;
+
+ $('.dataTableFooterIcons', domElem)
+ .hover( function() {
+ //display 'hand' cursor
+ $(this).css({ cursor: "pointer"});
+
+ //cancel timeout if necessary
+ if(timeout != null)
+ {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ },
+ function() {
+ //display standard cursor
+ $(this).css({ cursor: "auto"});
+
+ //set a timeout that will hide export buttons after a few moments
+ var dom = this;
+ timeout = setTimeout(function(){
+ $('.exportToFormatIcons', dom).fadeOut('fast', function(){ //queue the two actions
+ $('.dataTableFooterIconsShow', dom).show('fast');});
+ }, 1000);
+ }
+ );
+
+ $('.viewDataTable', domElem).click(
+ function(){
+ var viewDataTable = $(this).attr('format');
+ self.resetAllFilters();
+ self.param.viewDataTable = viewDataTable;
+ self.reloadAjaxDataTable();
+ }
+ );
+
+ $('.tableGoals', domElem)
+ .show()
+ .click(
+ function(){
+ // we only reset the limit filter, in case switch to table view from cloud view where limit is custom set to 30
+ // this value is stored in config file General->datatable_default_limit but this is more an edge case so ok to set it to 10
+ delete self.param.filter_limit;
+ delete self.param.enable_filter_excludelowpop;
+ self.param.viewDataTable = 'tableGoals';
+ self.reloadAjaxDataTable();
+ }
+ );
+
+ $('.tableAllColumnsSwitch', domElem)
+ .show()
+ .click(
+ function(){
+ // we only reset the limit filter, in case switch to table view from cloud view where limit is custom set to 30
+ // this value is stored in config file General->datatable_default_limit but this is more an edge case so ok to set it to 10
+ delete self.param.filter_limit;
+ self.param.viewDataTable = self.param.viewDataTable == 'table' ? 'tableAllColumns' : 'table';
+ // when switching to display simple table, do not exclude low pop by default
+ if(self.param.viewDataTable == 'table')
+ {
+ self.param.enable_filter_excludelowpop = 0;
+ }
+ self.reloadAjaxDataTable();
+ }
+ );
+
+ $('.exportToFormatIcons img', domElem).click(function(){
+ $(this).siblings('.linksExportToFormat').toggle();
+ });
+
+ $('.exportToFormat', domElem).attr( 'href', function(){
+ var format = $(this).attr('format');
+ var method = $(this).attr('methodToCall');
+ var filter_limit = $(this).attr('filter_limit');
+
+ var param_date = self.param.date;
+ var date = $(this).attr('date');
+ if(typeof date != 'undefined') {
+ param_date = date;
+ }
+ var str = 'index.php?module=API'
+ +'&method='+method
+ +'&format='+format
+ +'&idSite='+self.param.idSite
+ +'&period='+self.param.period
+ +'&date='+param_date
+ +'&token_auth='+piwik.token_auth;
+ if( filter_limit )
+ {
+ str += '&filter_limit=' + filter_limit;
+ }
+ return str;
+ }
+ );
+ },
+
+ truncate: function(domElemToTruncate, truncationOffset)
+ {
+ var self = this;
+
+ if(typeof truncationOffset == 'undefined') {
+ truncationOffset = 0;
+ }
+ var truncationLimit = 30;
+ // in a subtable
+ if(typeof self.param.idSubtable != 'undefined')
+ {
+ truncationLimit = 25;
+ }
+ // when showing all columns
+ if(typeof self.param.idSubtable == 'undefined'
+ && self.param.viewDataTable == 'tableAllColumns')
+ {
+ truncationLimit = 17;
+ }
+ // when showing all columns in a subtable, space is restricted
+ else if(self.param.viewDataTable == 'tableAllColumns')
+ {
+ truncationLimit = 10;
+ }
+
+ truncationLimit += truncationOffset;
+
+ $(domElemToTruncate).truncate(truncationLimit);
+ $('.truncated', domElemToTruncate)
+ .tooltip();
+ },
+
+ //Apply some miscelleaneous style to the DataTable
+ applyCosmetics: function(domElem)
+ {
+ var self = this;
+
+ // Add some styles on the cells even/odd
+ // label (first column of a data row) or not
+ $("th:first-child", domElem).addClass('label');
+ $("td:first-child:odd", domElem).addClass('label labeleven');
+ $("td:first-child:even", domElem).addClass('label labelodd');
+ $("tr:odd td", domElem).slice(1).addClass('columnodd');
+ $("tr:even td", domElem).slice(1).addClass('columneven');
+
+ },
+
+ //behaviour for 'nested DataTable' (DataTable loaded on a click on a row)
+ handleSubDataTable: function(domElem)
+ {
+ var self = this;
+ // When the TR has a subDataTable class it means that this row has a link to a subDataTable
+ $('tr.subDataTable', domElem)
+ .click(
+ function()
+ {
+ // get the idSubTable
+ var idSubTable = $(this).attr('id');
+ var divIdToReplaceWithSubTable = 'subDataTable_'+idSubTable;
+
+ // if the subDataTable is not already loaded
+ if (typeof self.loadedSubDataTable[divIdToReplaceWithSubTable] == "undefined")
+ {
+ var numberOfColumns = $(this).children().length;
+
+ // at the end of the query it will replace the ID matching the new HTML table #ID
+ // we need to create this ID first
+ $(this).after(
+ '
'+ _pk_translate('CoreHome_Loading_js') +''+
+ '
Loading...\
+ {$columnTranslations[$column]} |
+ {/foreach}
+
|---|
| +{if !$row.idsubdatatable && $column=='label' && !empty($row.metadata.url)}{$row.metadata.url}{/if} +{if $column=='label'}{logoHtml metadata=$row.metadata alt=$row.columns.label}{/if} +{if isset($row.columns[$column])}{$row.columns[$column]}{else}{$defaultWhenColumnValueNotDefined}{/if} + | +{/foreach} +
{if !empty($columnDescriptions[$column])}{/if}
+ {/foreach}
+ |
|---|
| + {if !$row.idsubdatatable && $column=='label' && isset($row.metadata.url)}{$row.metadata.url}{/if} + {if isset($row.columns[$column])}{$row.columns[$column]}{else}{$defaultWhenColumnValueNotDefined}{/if} + | + {/foreach} +
| {$columnTranslations[$column]} + {/foreach} + |
|---|
| + {if isset($row.columns[$column])}{$row.columns[$column]}{else}{$defaultWhenColumnValueNotDefined}{/if} + | + {/foreach} +
{'General_LoadingData'|translate}
+
');
+ }
+ }, function(){
+ restoreCurrentPeriod();
+ // we callback the function to hide the sub menu
+ // only if it was visible (otherwise it messes the state machine)
+ if($("#otherPeriods").is(":visible"))
+ {
+ timeout = setTimeout( removePeriodMenu , timeoutLength);
+ }
+ setCurrentPeriodStyle = false;
+ })
+ .click( function() {
+ // we restore the initial style on the DAY link
+ restoreCurrentPeriod();
+ // the menu shall fadeout after 500ms
+ timeoutLength = 500;
+ // appearance!
+ $("#otherPeriods").fadeIn();
+
+ });
+
+ $("#periodString #date")
+ .hover( function(){
+ $(this).css({ cursor: "pointer"});
+ }, function(){
+
+ })
+ .click(function(){
+ $("#datepicker").toggle();
+ if($("#datepicker").is(":visible"))
+ {
+ $("#datepicker .ui-state-highlight").removeClass('ui-state-highlight');
+ }
+ });
+} );
diff --git a/oss/piwik/trunk/plugins/CoreHome/templates/graph.tpl b/oss/piwik/trunk/plugins/CoreHome/templates/graph.tpl
new file mode 100644
index 00000000..eab4ef04
--- /dev/null
+++ b/oss/piwik/trunk/plugins/CoreHome/templates/graph.tpl
@@ -0,0 +1,48 @@
+
+ {if $isSuperUser}
+ {'General_PiwikXIsAvailablePleaseUpdateNow'|translate:$latest_version_available:"{'CorePluginsAdmin_MainDescription'|translate}
+| {'CorePluginsAdmin_Plugin'|translate} | +{'CorePluginsAdmin_Version'|translate} | +{'CorePluginsAdmin_Description'|translate} | +{'CorePluginsAdmin_Status'|translate} | +{'CorePluginsAdmin_Action'|translate} | +
|---|---|---|---|---|
| + {if isset($plugin.info.homepage)}{/if} + {$name} + {if isset($plugin.info.homepage)}{/if} + | +{$plugin.info.version} | ++ {$plugin.info.description|nl2br} + By + {if isset($plugin.info.author_homepage)}{/if} + {$plugin.info.author}{if isset($plugin.info.author_homepage)}{/if}. + | ++ {if $plugin.alwaysActivated}{'CorePluginsAdmin_Active'|translate} + {elseif $plugin.activated}{'CorePluginsAdmin_Active'|translate} + {else}{'CorePluginsAdmin_Inactive'|translate}{/if} + | + +
+ {if $plugin.alwaysActivated} |
+
php ".Piwik_Common::getPathToPiwikRoot()."/index.php -- \"module=CoreUpdater\" ";
+ $pluginNamesToUpdate = array();
+ $coreToUpdate = false;
+
+ // handle case of existing database with no tables
+ if(!Piwik::isInstalled())
+ {
+ $this->errorMessages[] = Piwik_Translate('CoreUpdater_EmptyDatabaseError', Zend_Registry::get('config')->database->dbname);
+ $this->coreError = true;
+ $currentVersion = 'N/A';
+ }
+ else
+ {
+ $this->errorMessages = array();
+ try {
+ $currentVersion = Piwik_GetOption('version_core');
+ } catch( Exception $e) {
+ $currentVersion = '<= 0.2.9';
+ }
+
+ foreach($componentsWithUpdateFile as $name => $filenames)
+ {
+ if($name == 'core')
+ {
+ $coreToUpdate = true;
+ }
+ else
+ {
+ $pluginNamesToUpdate[] = $name;
+ }
+ }
+ }
+
+ // check file integrity
+ $integrityInfo = Piwik::getFileIntegrityInformation();
+ if(isset($integrityInfo[1]))
+ {
+ if($integrityInfo[0] == false)
+ {
+ $this->warningMessages[] = ''.Piwik_Translate('General_FileIntegrityWarningExplanation').'';
+ }
+ $this->warningMessages = array_merge($this->warningMessages, array_slice($integrityInfo, 1));
+ }
+
+ $view->coreError = $this->coreError;
+ $view->warningMessages = $this->warningMessages;
+ $view->errorMessages = $this->errorMessages;
+ $view->current_piwik_version = $currentVersion;
+ $view->pluginNamesToUpdate = $pluginNamesToUpdate;
+ $view->coreToUpdate = $coreToUpdate;
+ $view->clearCompiledTemplates();
+ echo $view->render();
+ }
+
+ private function doExecuteUpdates($view, $updater, $componentsWithUpdateFile)
+ {
+ $this->loadAndExecuteUpdateFiles($updater, $componentsWithUpdateFile);
+
+ $view->coreError = $this->coreError;
+ $view->warningMessages = $this->warningMessages;
+ $view->errorMessages = $this->errorMessages;
+ $view->deactivatedPlugins = $this->deactivatedPlugins;
+ $view->clearCompiledTemplates();
+ echo $view->render();
+ }
+
+ private function loadAndExecuteUpdateFiles($updater, $componentsWithUpdateFile)
+ {
+ // if error in any core update, show message + help message + EXIT
+ // if errors in any plugins updates, show them on screen, disable plugins that errored + CONTINUE
+ // if warning in any core update or in any plugins update, show message + CONTINUE
+ // if no error or warning, success message + CONTINUE
+ foreach($componentsWithUpdateFile as $name => $filenames)
+ {
+ try {
+ $this->warningMessages = array_merge($this->warningMessages, $updater->update($name));
+ } catch (Piwik_Updater_UpdateErrorException $e) {
+ $this->errorMessages[] = $e->getMessage();
+ if($name == 'core')
+ {
+ $this->coreError = true;
+ break;
+ }
+ else
+ {
+ Piwik_PluginsManager::getInstance()->deactivatePlugin($name);
+ $this->deactivatedPlugins[] = $name;
+ }
+ }
+ }
+ }
+}
diff --git a/oss/piwik/trunk/plugins/CoreUpdater/CoreUpdater.php b/oss/piwik/trunk/plugins/CoreUpdater/CoreUpdater.php
new file mode 100644
index 00000000..3bef77a2
--- /dev/null
+++ b/oss/piwik/trunk/plugins/CoreUpdater/CoreUpdater.php
@@ -0,0 +1,71 @@
+ Piwik_Translate('CoreUpdater_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'FrontController.dispatchCoreAndPluginUpdatesScreen' => 'dispatch',
+ 'FrontController.checkForUpdates' => 'updateCheck',
+ );
+ return $hooks;
+ }
+
+ public static function getComponentUpdates($updater)
+ {
+ $updater->addComponentToCheck('core', Piwik_Version::VERSION);
+
+ $plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
+ foreach($plugins as $pluginName => $plugin)
+ {
+ $updater->addComponentToCheck($pluginName, $plugin->getVersion());
+ }
+
+ $componentsWithUpdateFile = $updater->getComponentsWithUpdateFile();
+ if(count($componentsWithUpdateFile) == 0 && !$updater->hasNewVersion('core'))
+ {
+ return null;
+ }
+
+ return $componentsWithUpdateFile;
+ }
+
+ function dispatch()
+ {
+ $module = Piwik_Common::getRequestVar('module', '', 'string');
+ $updater = new Piwik_Updater();
+ if(self::getComponentUpdates($updater) !== null && $module != 'CoreUpdater')
+ {
+ Piwik::redirectToModule('CoreUpdater');
+ }
+ }
+
+ function updateCheck()
+ {
+ Piwik_UpdateCheck::check();
+ }
+}
diff --git a/oss/piwik/trunk/plugins/CoreUpdater/templates/cli_update_database_done.tpl b/oss/piwik/trunk/plugins/CoreUpdater/templates/cli_update_database_done.tpl
new file mode 100644
index 00000000..6cda000c
--- /dev/null
+++ b/oss/piwik/trunk/plugins/CoreUpdater/templates/cli_update_database_done.tpl
@@ -0,0 +1,62 @@
+{textformat}
+{assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'[':']':"\n\n* "|unescape}
+
+{if $coreError}
+ [X] {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate|unescape}
+
+ {foreach from=$errorMessages item=message}
+ * {$message}
+
+ {/foreach}
+
+ {'CoreUpdater_HelpMessageIntroductionWhenError'|translate|unescape}
+
+ * {$helpMessage}
+
+
+ {'CoreUpdater_ErrorDIYHelp'|translate}
+
+ * {'CoreUpdater_ErrorDIYHelp_1'|translate}
+
+ * {'CoreUpdater_ErrorDIYHelp_2'|translate}
+
+ * {'CoreUpdater_ErrorDIYHelp_3'|translate}
+
+ * {'CoreUpdater_ErrorDIYHelp_4'|translate}
+
+ * {'CoreUpdater_ErrorDIYHelp_5'|translate}
+
+{else}
+ {if count($warningMessages) > 0}
+ [!] {'CoreUpdater_WarningMessages'|translate|unescape}
+
+ {foreach from=$warningMessages item=message}
+ * {$message}
+
+ {/foreach}
+ {/if}
+
+ {if count($errorMessages) > 0}
+ [X] {'CoreUpdater_ErrorDuringPluginsUpdates'|translate|unescape}
+
+ {foreach from=$errorMessages item=message}
+ * {$message}
+
+ {/foreach}
+
+ {if isset($deactivatedPlugins) && count($deactivatedPlugins) > 0}
+ {assign var=listOfDeactivatedPlugins value=$deactivatedPlugins|@implode:', '}
+ [!] {'CoreUpdater_WeAutomaticallyDeactivatedTheFollowingPlugins'|translate:$listOfDeactivatedPlugins|unescape}
+
+ {/if}
+ {/if}
+ {if count($errorMessages) > 0 || count($warningMessages) > 0}
+ {'CoreUpdater_HelpMessageIntroductionWhenWarning'|translate|unescape}
+
+ * {$helpMessage}
+ {else}
+ {'CoreUpdater_PiwikHasBeenSuccessfullyUpgraded'|translate|unescape}
+
+ {/if}
+{/if}
+{/textformat}
diff --git a/oss/piwik/trunk/plugins/CoreUpdater/templates/cli_update_welcome.tpl b/oss/piwik/trunk/plugins/CoreUpdater/templates/cli_update_welcome.tpl
new file mode 100644
index 00000000..28d60bd9
--- /dev/null
+++ b/oss/piwik/trunk/plugins/CoreUpdater/templates/cli_update_welcome.tpl
@@ -0,0 +1,36 @@
+{assign var='helpMessage' value='CoreUpdater_HelpMessageContent'|translate:'[':']':"\n\n* "|unescape}
+{textformat}
+*** {'CoreUpdater_UpdateTitle'|translate|unescape} ***
+
+{if $coreError}
+ [X] {'CoreUpdater_CriticalErrorDuringTheUpgradeProcess'|translate|unescape}
+
+ {foreach from=$errorMessages item=message}
+ * {$message}
+
+ {/foreach}
+
+ {'CoreUpdater_HelpMessageIntroductionWhenError'|translate|unescape}
+
+ * {$helpMessage}
+
+{else}
+ {if $coreToUpdate || count($pluginNamesToUpdate) > 0}
+ {'CoreUpdater_DatabaseUpgradeRequired'|translate|unescape}
+
+ {'CoreUpdater_YourDatabaseIsOutOfDate'|translate|unescape}
+
+ {if $coreToUpdate}
+ {'CoreUpdater_PiwikWillBeUpgradedFromVersionXToVersionY'|translate:$current_piwik_version:$new_piwik_version|unescape}
+
+ {/if}
+ {if count($pluginNamesToUpdate) > 0}
+ {assign var=listOfPlugins value=$pluginNamesToUpdate|@implode:', '}
+ {'CoreUpdater_TheFollowingPluginsWillBeUpgradedX'|translate:$listOfPlugins|unescape}
+
+ {/if}
+ {'CoreUpdater_TheUpgradeProcessMayTakeAWhilePleaseBePatient'|translate|unescape}
+
+ {/if}
+{/if}
+{/textformat}
diff --git a/oss/piwik/trunk/plugins/CoreUpdater/templates/footer.tpl b/oss/piwik/trunk/plugins/CoreUpdater/templates/footer.tpl
new file mode 100644
index 00000000..9943ff0f
--- /dev/null
+++ b/oss/piwik/trunk/plugins/CoreUpdater/templates/footer.tpl
@@ -0,0 +1,3 @@
+
+