diff options
author | mattpiwik <matthieu.aubry@gmail.com> | 2008-07-28 03:32:54 +0400 |
---|---|---|
committer | mattpiwik <matthieu.aubry@gmail.com> | 2008-07-28 03:32:54 +0400 |
commit | 995c69fefeb52d289e3613e552ce11d204826d8e (patch) | |
tree | ab59bac1803b272272ca9d16ae69b65fd44a25ba | |
parent | 9bcf3f24892e3ce52e0ce9cb4c38465b9084059c (diff) |
- end of previous commit. For some reasons Tortoise didn't see these files, bad!
git-svn-id: http://dev.piwik.org/svn/trunk@582 59fd770c-687e-43c8-a1e3-f5a4ff64c105
43 files changed, 3187 insertions, 0 deletions
diff --git a/plugins/CoreAdminHome/Controller.php b/plugins/CoreAdminHome/Controller.php new file mode 100644 index 0000000000..9867b324ad --- /dev/null +++ b/plugins/CoreAdminHome/Controller.php @@ -0,0 +1,66 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version + * + * @package Piwik_CoreAdminHome + * + */ + +require_once "API/Request.php"; + + +/** + * + * @package Piwik_CoreAdminHome + */ +class Piwik_CoreAdminHome_Controller extends Piwik_Controller +{ + function getDefaultAction() + { + return 'redirectToIndex'; + } + function redirectToIndex() + { + header("Location:index.php?module=CoreAdminHome&action=showInContext&moduleToLoad=CorePluginsAdmin"); + } + + public function showInContext() + { + $controllerName = Piwik_Common::getRequestVar('moduleToLoad'); + $actionName = Piwik_Common::getRequestVar('actionToLoad', 'index'); + + $view = $this->getDefaultIndexView(); + $view->basicHtmlView = true; + $view->content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName ); + echo $view->render(); + } + + public function index() + { + Piwik::checkUserIsSuperUser(); + + $view = $this->getDefaultIndexView(); + echo $view->render(); + } + + protected function getDefaultIndexView() + { + $view = new Piwik_View('CoreAdminHome/templates/index.tpl'); + $view->menu = Piwik_GetAdminMenu(); + $view->menuJson = json_encode($view->menu); + + $view->userLogin = Piwik::getCurrentUserLogin(); + $view->token_auth = Piwik::getCurrentUserTokenAuth(); + $view->sites = Piwik_SitesManager_API::getSitesWithAtLeastViewAccess(); + $view->url = Piwik_Url::getCurrentUrl(); + + $view->basicHtmlView = false; + $view->content = ''; + return $view; + } +} + diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php new file mode 100644 index 0000000000..04fa9d7796 --- /dev/null +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -0,0 +1,27 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id: ExamplePlugin.php 169 2008-01-14 05:41:15Z matt $ + * + * @package Piwik_CoreAdminHome + */ + + +class Piwik_CoreAdminHome extends Piwik_Plugin +{ + public function getInformation() + { + return array( + // name must be the className prefix! + 'name' => 'CoreAdminHome', + 'description' => 'Administration area of Piwik.', + 'author' => 'Piwik', + 'homepage' => 'http://piwik.org/', + 'version' => '0.1', + ); + } +} + diff --git a/plugins/CoreAdminHome/templates/index.tpl b/plugins/CoreAdminHome/templates/index.tpl new file mode 100644 index 0000000000..6a13043af7 --- /dev/null +++ b/plugins/CoreAdminHome/templates/index.tpl @@ -0,0 +1,53 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+{literal}
+<style>
+#h1, #h1 a {
+ color: #136F8B;
+ font-size: 45px;
+ font-weight: lighter;
+ text-decoration : none;
+ margin:5px;
+}
+
+#subh1 {
+ color: #879dbd;
+ font-size: 25px;
+ font-weight: lighter;
+}
+</style>
+{/literal}
+
+<script type="text/javascript">
+var piwik_token_auth = "{$token_auth}";
+</script>
+
+<script type="text/javascript" src="libs/jquery/jquery.js"></script>
+<script type="text/javascript" src="themes/default/common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="plugins/CoreAdminHome/templates/menu.css">
+<link rel="stylesheet" href="themes/default/common-admin.css">
+
+</head>
+<body>
+<span id="h1">Piwik admin</span>
+<span><a href='index.php'>Back to Piwik</a></span>
+<br /><br />
+
+<div id="menu">
+{include file="CoreAdminHome/templates/menu.tpl"}
+</div>
+
+<div style="clear:both;">
+</div>
+
+<div id='content'>
+{if $content}{$content}{/if}
+</div>
+
+<div id="footer" style="border-top:1px solid gray; margin-top:20px;padding-top:10px;">
+<a href='?module=CoreHome'>{'General_BackToHomepage'|translate}</a>
+
+</div>
diff --git a/plugins/CoreAdminHome/templates/menu.css b/plugins/CoreAdminHome/templates/menu.css new file mode 100644 index 0000000000..312ba8a436 --- /dev/null +++ b/plugins/CoreAdminHome/templates/menu.css @@ -0,0 +1,37 @@ +#tablist { + padding: 3px 0; + margin-left: 0; + margin-bottom: 0; + margin-top: 0.1em; + border-bottom: 1px solid gray; +} + +#tablist li { + list-style: none; + display: inline; + margin: 0; +} + +#tablist li a { + text-decoration: none; + padding: 3px 0.5em; + margin-right: 3px; + border: 1px solid #778; + border-bottom: none; + background: white; + font: bold 16px Georgia; +} + +#tablist li a:link,#tablist li a:visited { + color: black; +} + +#tablist li a:hover { + color: black; + background: #defdbb; + border-color: black; +} + +#tablist li a.current { + background: #defdbb; +}
\ No newline at end of file diff --git a/plugins/CoreAdminHome/templates/menu.tpl b/plugins/CoreAdminHome/templates/menu.tpl new file mode 100644 index 0000000000..0922c2e05b --- /dev/null +++ b/plugins/CoreAdminHome/templates/menu.tpl @@ -0,0 +1,5 @@ +<ul id="tablist"> +{foreach from=$menu key=name item=url name=menu} + <li><a name='{$url|@urlRewriteAdminView}' href='{$url|@urlRewriteAdminView}'>{$name}</a></li> +{/foreach} +</ul> diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php new file mode 100644 index 0000000000..88c992e9e5 --- /dev/null +++ b/plugins/CoreHome/Controller.php @@ -0,0 +1,134 @@ +<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later
+ * @version $Id: Controller.php 561 2008-07-21 00:00:35Z matt $
+ *
+ * @package Piwik_CoreHome
+ *
+ */
+
+
+require_once "API/Request.php";
+require_once "ViewDataTable.php";
+
+/**
+ *
+ * @package Piwik_CoreHome
+ */
+class Piwik_CoreHome_Controller extends Piwik_Controller
+{
+ function getDefaultAction()
+ {
+ return 'redirectToIndex';
+ }
+ function redirectToIndex()
+ {
+ $sitesId = Piwik_SitesManager_API::getSitesIdWithAtLeastViewAccess();
+ if(!empty($sitesId))
+ {
+ $firstSiteId = $sitesId[0];
+ $firstSite = new Piwik_Site($firstSiteId);
+ if ($firstSite->getCreationDate()->isToday())
+ {
+ $defaultDate = 'today';
+ }
+ else
+ {
+ $defaultDate = Zend_Registry::get('config')->General->default_day;
+ }
+ header("Location:index.php?module=CoreHome&action=index&idSite=$firstSiteId&period=day&date=$defaultDate");
+ }
+ else
+ {
+ if(($currentLogin = Piwik::getCurrentUserLogin()) != 'anonymous')
+ {
+ Piwik_ExitWithMessage( sprintf(Piwik_Translate('CoreHome_NoPrivileges'),$currentLogin).
+ "<br /><br /> <b><a href='?module=Login&action=logout'>› ".Piwik_Translate('General_Logout')."</a></b><br />");
+ }
+ else
+ {
+ Piwik_FrontController::dispatch('Login');
+ }
+ }
+ exit;
+ }
+
+ protected function setGeneralVariablesView($view)
+ {
+ // date
+ $view->date = $this->strDate;
+ $oDate = Piwik_Date::factory($this->strDate);
+ $localizedDateFormat = Piwik_Translate('CoreHome_LocalizedDateFormat');
+ $view->prettyDate = $oDate->getLocalized($localizedDateFormat);
+
+ // period
+ $currentPeriod = Piwik_Common::getRequestVar('period');
+ $otherPeriodsAvailable = array('day', 'week', 'month', 'year');
+
+ $otherPeriodsNames = array(
+ 'day' => Piwik_Translate('CoreHome_PeriodDay'),
+ 'week' => Piwik_Translate('CoreHome_PeriodWeek'),
+ 'month' => Piwik_Translate('CoreHome_PeriodMonth'),
+ 'year' => Piwik_Translate('CoreHome_PeriodYear')
+ );
+
+ $found = array_search($currentPeriod,$otherPeriodsAvailable);
+ if($found !== false)
+ {
+ unset($otherPeriodsAvailable[$found]);
+ }
+
+ $view->period = $currentPeriod;
+ $view->otherPeriods = $otherPeriodsAvailable;
+ $view->periodsNames = $otherPeriodsNames;
+
+ // other
+ $view->idSite = Piwik_Common::getRequestVar('idSite');
+
+ $view->userLogin = Piwik::getCurrentUserLogin();
+ $view->sites = Piwik_SitesManager_API::getSitesWithAtLeastViewAccess();
+ $view->url = Piwik_Url::getCurrentUrl();
+
+ $view->menu = Piwik_GetMenu();
+ $view->menuJson = json_encode($view->menu);
+ //var_dump($view->menuJson);
+ }
+
+ public function showInContext()
+ {
+ $controllerName = Piwik_Common::getRequestVar('moduleToLoad');
+ $actionName = Piwik_Common::getRequestVar('actionToLoad', 'index');
+
+ $view = $this->getDefaultIndexView();
+ $view->basicHtmlView = true;
+ $view->content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName );
+ echo $view->render();
+ }
+
+ protected function getDefaultIndexView()
+ {
+
+ $view = new Piwik_View('CoreHome/templates/index.tpl');
+ $this->setGeneralVariablesView($view);
+
+ $site = new Piwik_Site($view->idSite);
+ $minDate = $site->getCreationDate();
+
+ $view->minDateYear = $minDate->toString('Y');
+ $view->minDateMonth = $minDate->toString('m');
+ $view->minDateDay = $minDate->toString('d');
+
+ $view->basicHtmlView = false;
+ $view->content = '';
+ return $view;
+ }
+ public function index()
+ {
+ $view = $this->getDefaultIndexView();
+ echo $view->render();
+ }
+}
+
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php new file mode 100644 index 0000000000..37891b5e3c --- /dev/null +++ b/plugins/CoreHome/CoreHome.php @@ -0,0 +1,26 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id: ExamplePlugin.php 169 2008-01-14 05:41:15Z matt $ + * + * @package Piwik_CorePluginsAdmin + */ + +class Piwik_CoreHome extends Piwik_Plugin +{ + public function getInformation() + { + return array( + // name must be the className prefix! + 'name' => 'CoreHome', + 'description' => 'Web Analytics Reports Structure.', + 'author' => 'Piwik', + 'homepage' => 'http://piwik.org/', + 'version' => '0.1', + ); + } +} + diff --git a/plugins/CoreHome/templates/calendar.js b/plugins/CoreHome/templates/calendar.js new file mode 100644 index 0000000000..a52f856063 --- /dev/null +++ b/plugins/CoreHome/templates/calendar.js @@ -0,0 +1,172 @@ + +Date.prototype.getWeek = function() { +var onejan = new Date(this.getFullYear(),0,1); +return Math.ceil((((this - onejan) / 86400000) + onejan.getDay())/7); +} + +var splitDate = currentDateStr.split("-"); + +var currentYear = splitDate[0]; +var currentMonth = splitDate[1] - 1; +var currentDay = splitDate[2]; + +var currentDate = new Date(currentYear, currentMonth, currentDay); +var todayDate = new Date; +var todayMonth = todayDate.getMonth(); +var todayYear = todayDate.getFullYear(); +var todayDay = todayDate.getDate(); + +function isDateSelected( date ) +{ + var valid = false; + + var dateMonth = date.getMonth(); + var dateYear = date.getFullYear(); + var dateDay = date.getDate(); + var style = ''; + + if( date.toLocaleDateString() == todayDate.toLocaleDateString()) + { + style = style + 'dateToday '; + } + + // we dont color dates in the future + if( dateMonth == todayMonth + && dateYear == todayYear + && dateDay >= todayDay + ) + { + return [true, style]; + } + + // we dont color dates before the minimum date + if( dateYear < minDateYear + || ( dateYear == minDateYear + && + ( + (dateMonth == minDateMonth - 1 + && dateDay < minDateDay) + || (dateMonth < minDateMonth - 1) + ) + ) + ) + { + return [true, style]; + } + + // we color all day of the month for the same year for the month period + if(period == "month" + && dateMonth == currentMonth + && dateYear == currentYear + ) + { + valid = true; + } + // we color all day of the year for the year period + else if(period == "year" + && dateYear == currentYear + ) + { + valid = true; + } + else if(period == "week" + && date.getWeek() == currentDate.getWeek() + && dateYear == currentYear + ) + { + valid = true; + } + else if( period == "day" + && dateDay == currentDay + && dateMonth == currentMonth + && dateYear == currentYear + ) + { + valid = true; + } + + if(valid) + { + return [true, style+'dateUsedStats']; + } + return [true, style]; +} + + +function updateDate() +{ + var date = formatDate(popUpCal.getDateFor($('#calendar')[0])); + + // available in global scope + var currentUrl = window.location.href; + if((startStrDate = currentUrl.indexOf("date")) >= 0) + { + // look for the & after the date + var endStrDate = currentUrl.indexOf("&", startStrDate); + if(endStrDate == -1) + { + endStrDate = currentUrl.length; + } + + var dateToReplace = currentUrl.substring( + startStrDate + 4+1, + endStrDate + ); + regDateToReplace = new RegExp(dateToReplace, 'ig'); + currentUrl = currentUrl.replace( regDateToReplace, date ); + } + else + { + currentUrl = currentUrl + '&date=' + date; + } + + window.location.href = currentUrl; +} + +function formatDate(date) +{ + var day = date.getDate(); + var month = date.getMonth() + 1; + return date.getFullYear() + '-' + + (month < 10 ? '0' : '') + month + '-' + + (day < 10 ? '0' : '') + day ; +} + +$(document).ready(function(){ + $("#calendar").calendar({ + onSelect: updateDate, + showOtherMonths: true, + dateFormat: 'DMY-', + firstDay: 1, + minDate: new Date(minDateYear, minDateMonth - 1, minDateDay), + maxDate: new Date(), + changeFirstDay: false, + prevText: "", + nextText: "", + currentText: "", + customDate: isDateSelected, + dayNames: [ + _pk_translate('CoreHome_DaySu'), + _pk_translate('CoreHome_DayMo'), + _pk_translate('CoreHome_DayTu'), + _pk_translate('CoreHome_DayWe'), + _pk_translate('CoreHome_DayTh'), + _pk_translate('CoreHome_DayFr'), + _pk_translate('CoreHome_DaySa')], + monthNames: [ + _pk_translate('CoreHome_MonthJanuary'), + _pk_translate('CoreHome_MonthFebruary'), + _pk_translate('CoreHome_MonthMarch'), + _pk_translate('CoreHome_MonthApril'), + _pk_translate('CoreHome_MonthMay'), + _pk_translate('CoreHome_MonthJune'), + _pk_translate('CoreHome_MonthJuly'), + _pk_translate('CoreHome_MonthAugust'), + _pk_translate('CoreHome_MonthSeptember'), + _pk_translate('CoreHome_MonthOctober'), + _pk_translate('CoreHome_MonthNovember'), + _pk_translate('CoreHome_MonthDecemeber')] + }, + currentDate); + } +); diff --git a/plugins/CoreHome/templates/cloud.tpl b/plugins/CoreHome/templates/cloud.tpl new file mode 100644 index 0000000000..91b3913819 --- /dev/null +++ b/plugins/CoreHome/templates/cloud.tpl @@ -0,0 +1,68 @@ +<div id="{$id}" class="parentDiv"> +{literal} +<style> + +#tagCloud{ + width:100%; +} +img { + border:0; +} +.word a { + text-decoration:none; +} +.word { + padding: 4px 4px 4px 4px; +} +.valueIsZero { + text-decoration: line-through; +} +span.size0, span.size0 a { + color: #344971; + font-size: 28px; +} +span.size1, span.size1 a { + color: #344971; + font-size: 24px; +} +span.size2, span.size2 a { + color: #4B74AD; + font-size:20px; +} +span.size3, span.size3 a { + color: #A3A8B6; + font-size: 16px; +} +span.size4, span.size4 a { + color: #A3A8B6; + font-size: 15px; +} +span.size5, span.size5 a { + color: #A3A8B6; + font-size: 14px; +} +span.size6, span.size6 a { + color: #A3A8B6; + font-size: 11px; +} +</style> +{/literal} + +<div id="tagCloud"> +{if count($cloudValues) == 0} + <div id="emptyDatatable">No data for this tag cloud.</div> +{else} + {foreach from=$cloudValues key=word item=value} + <span title="{$value.word} ({$labelMetadata[$value.word].hits} hits)" class="word size{$value.size} {* we strike tags with 0 hits *} {if $labelMetadata[$value.word].hits == 0}valueIsZero{/if}"> + {if false !== $labelMetadata[$value.word].url}<a href="{$labelMetadata[$value.word].url}" target="_blank">{/if} + {if false !== $labelMetadata[$value.word].logo}<img src="{$labelMetadata[$value.word].logo}" width="{$value.logoWidth}">{else} + + {$value.wordTruncated}{/if}{if false !== $labelMetadata[$value.word].url}</a>{/if}</span> + {/foreach} +{/if} +{if $showFooter} + {include file="CoreHome/templates/datatable_footer.tpl"} +{/if} +{include file="CoreHome/templates/datatable_js.tpl"} +</div> +</div> diff --git a/plugins/CoreHome/templates/datatable.css b/plugins/CoreHome/templates/datatable.css new file mode 100644 index 0000000000..bfd266b599 --- /dev/null +++ b/plugins/CoreHome/templates/datatable.css @@ -0,0 +1,339 @@ +/* container of each table */ +.parentDiv { + width: 450px; + /* not more than 450px to make sure 2 of them can fit horizontally on a 1024 screen */ +} + +.parentDivActions { + width: 500px; +} + +.parentDivGraph { + width: 500px; +} + +.parentDivGraphEvolution { + width: 100%; +} + +/* main data table */ +table.dataTable { + width: 100%; + padding: 0; + border-spacing: 0; + margin: 0; + font-size: 0.9em; +} + +table.dataTable td.label,table.subDataTable td.label,table.dataTableActions td.label + { + width: 100%; +} + +table.dataTable img,table.subDataTable img,table.dataTableActions img { + vertical-align: middle; +} + +table.dataTable img { + border: 0; + margin-right: 1em; + margin-left: 0.5em; +} + +table.dataTable tr.subDataTable { + cursor: pointer; +} + +table.dataTable th { + margin: 0; + color: #6D929B; + border-right: 1px solid #C1DAD7; + border-bottom: 1px solid #C1DAD7; + border-top: 1px solid #C1DAD7; + text-transform: capitalize; + text-align: left; + padding: 6px 6px 6px 12px; + background: #D4E3ED url(images/bg_header.jpg) no-repeat; +} + +table.dataTable th.columnSorted { + font-weight: bold; + padding-right: 20px; +} + +table.dataTable td { + border-right: 1px solid #C1DAD7; + border-bottom: 1px solid #C1DAD7; + border-left: 0; + padding: 5px 5px 5px 12px; + background: #fff; +} + +table.dataTable td,table.dataTable td a { + margin: 0; + text-decoration: none; + color: #4f6b72; +} + +table.dataTable td.labeleven,table.dataTable td.columneven { + background: #F9FAFA; +} + +table.dataTable td.columneven { + color: #797268; +} + +table.dataTable td.labeleven { + background-image: url(images/bullet2.gif); + background-repeat: no-repeat; + color: #797268; +} + +table.dataTable td.labelodd { + background: #fff url(images/bullet1.gif) no-repeat; +} + +table.dataTable td.label,table.subActionsDataTable td.label,table.actionsDataTable td.label + { + border-top: 0; + border-left: 1px solid #C1DAD7; +} + +table.dataTable th.label { + border-left: 1px solid #C1DAD7; +} + +/* the cell containing the subdatatable */ +table.dataTable .cellSubDataTable { + border-left: 1px solid #C1DAD7; + padding: 0; + margin: 0; +} + +/* A link in a column in the DataTable */ +table.dataTable td #urlLink { + display: none; +} + +/* SUBDATATABLE */ /* a datatable inside another datatable */ +table.subDataTable { + background: #FFFFFF; + width: 95%; + border-top: 1px solid #e5eff8; + border-right: 1px solid #e5eff8; + margin: 10px; +} + +table.subDataTable td { + border: 0; +} + +table.subDataTable thead th { + font-weight: normal; + font-size: 1.1em; + text-align: left; + border: 0; + border: 1px solid #D1D1D1; + padding: .3em 1em; + color: #333333; + background: #FFE9C6; +} + +table.subDataTable td.labeleven,table.subDataTable td.labelodd { + background-image: none; +} + +table.subDataTable td { + border-bottom: 1px solid #E5E5E5; + border-left: 1px solid #E5E5E5; +} + +table.subDataTable td,table.subDataTable td a { + padding: 0.3em 1em; + color: #615B53; +} + +table.subDataTable td.labeleven,table.subDataTable td.columneven { + color: #2D2A27; +} + +table.subDataTable td.label { + width: 80%; +} + +table.subDataTable td.labelodd,table.subDataTable td.labelodd a { + background: #ffffff; +} + +/* misc SPAN and DIV */ +table thead div { + +} + +#sortIconContainer { + float: right; +} + +#sortIcon { + margin: 0px; + position: absolute; +} + +#dataTablePages { + color: #E1E1E1; + font-weight: bold; + margin: 10px; + font-size: 0.9em; +} + +#dataTableSearchPattern { + display: inline; + white-space: nowrap; +} + +#dataTableSearchPattern input { + font-size: 0.7em; + padding: 2px; + border: 1px solid #B3B3B3; + color: #0C183A; +} + +#dataTableSearchPattern input:hover { + background: #F7F7FF none repeat scroll 0%; +} + +#dataTableSearchPattern #keyword { + background: transparent url(images/search.png) no-repeat scroll 4px + center; + padding: 3px 3px 3px 20px; +} + +#dataTableExcludeLowPopulation,#dataTableNext,#dataTablePrevious { + font-size: 0.9em; + color: #184A83; + text-decoration: underline; + cursor: pointer; +} + +.subDataTable#dataTableFeatures { + padding-top: 0px; + padding-bottom: 5px; + width: 100%; +} + +#dataTableFeatures { + padding-top: 10px; + padding-bottom: 10px; + width: 100%; + text-align: center; +} + +#dataTableExcludeLowPopulation { + float: right; + font-size: 0.8em; + color: #C3C6D8; + text-align: right; +} + +#dataTableNext,#dataTablePrevious,#dataTableSearchPattern,#loadingDataTable + { + display: none; +} + +.subDataTable #exportDataTable { + height: 0px; +} + +#exportDataTable { + float: right; + height: 18px; +} + +#exportToFormat { + float: right; +} + +#exportDataTableShow { + float: right; +} + +#exportDataTable,#exportDataTable a { + text-decoration: none; + color: #8894B1; +} + +.dataTableSpacer { + clear: both; +} + +#loadingDataTable { + float: left; + font-size: 1em; + color: #193B6C; + padding: 0.5em; +} + +#tooltip { + position: absolute; + z-index: 3000; + border: 1px solid #111; + background-color: #eee; + padding-left: 5px; + padding-right: 5px; + opacity: 0.85; + font-size: 0.7em; +} + +/* Actions table */ +table.dataTableActions tr td.labelodd { + background-image: none; +} + +/* levels higher than 4 have a default padding left */ +tr.subActionsDataTable td.label,tr.actionsDataTable td.label { + padding-left: 7em; +} + +tr.level0 td.label { + padding-left: +1.5em; +} + +tr.level1 td.label { + padding-left: +3.5em; +} + +tr.level2 td.label { + padding-left: +5.5em; +} + +tr.level3 td.label { + padding-left: +6.5em; +} + +tr.level4 td.label { + padding-left: +7em; +} + +tr td.label img.plusMinus { + margin-left: -1em; + margin-right: 0em; +} + +/* graph form containing embed code */ +.formEmbedCode,.formEmbedCode input,.formEmbedCode a { + font-size: 11px; + text-decoration: none; +} + +.formEmbedCode input { + background-color: #FBFDFF; + border: 1px solid #ECECEC; +} + +#emptyDatatable { + padding-top: 20px; + padding-bottom: 10px; + text-align: center; + font-size: 0.9em; + font-style: italic; +}
\ No newline at end of file diff --git a/plugins/CoreHome/templates/datatable.js b/plugins/CoreHome/templates/datatable.js new file mode 100644 index 0000000000..3fd9fe85db --- /dev/null +++ b/plugins/CoreHome/templates/datatable.js @@ -0,0 +1,988 @@ +//----------------------------------------------------------------------------- +// Data Table +//----------------------------------------------------------------------------- +//A list of all our DataTables +//Test if the object have already been initialized (multiple includes) +if(typeof dataTables == "undefined") + var dataTables = new Object; + +//DataTable constructor +function dataTable() +{ + this.param = new Object; +} + +//Prototype of the DataTable object +dataTable.prototype = +{ + //initialisation function + init: function(workingDivId, domElem) + { + if(typeof domElem == "undefined") + { + domElem = $('#'+workingDivId); + } + + this.workingDivId = workingDivId; + this.loadedSubDataTable = new Object; + this.bindEventsAndApplyStyle(domElem); + this.initialized = true; + }, + + //function triggered when user click on column sort + onClickSort: function(domElem) + { + var self = this; + + var newColumnToSort = $(domElem).attr('id'); + // we lookup if the column to sort was already this one, if it is the case then we switch from desc <-> asc + if(self.param.filter_sort_column == newColumnToSort) + { + // toggle the sorted order + if(this.param.filter_sort_order == 'asc') + { + self.param.filter_sort_order = 'desc'; + } + else + { + self.param.filter_sort_order = 'asc'; + } + } + self.param.filter_offset = 0; + self.param.filter_sort_column = newColumnToSort; + self.reloadAjaxDataTable(); + }, + + //Reset DataTable filters (used before a reload or view change) + resetAllFilters: function() + { + var self = this; + var FiltersToRestore = new Array(); + filters = [ + 'filter_column', + 'filter_pattern', + 'filter_column_recursive', + 'filter_pattern_recursive', + 'filter_excludelowpop', + 'filter_excludelowpop_value', + 'filter_offset', + 'filter_limit', + 'filter_sort_column', + 'filter_sort_order', + 'show_search' + ]; + + for(var key in filters) + { + var value = filters[key]; + FiltersToRestore[value] = self.param[value]; + delete self.param[value]; + } + + + return FiltersToRestore; + }, + + //Restores the filters to the values given in the array in parameters + restoreAllFilters: function(FiltersToRestore) + { + var self = this; + for(key in FiltersToRestore) + { + self.param[key] = FiltersToRestore[key]; + } + }, + + //Translate string parameters to javascript builtins + //'true' -> true, 'false' -> false + //it simplifies condition tests in the code + cleanParams: function() + { + var self = this; + for(var key in self.param) + { + if(self.param[key] == 'true') self.param[key]=true; + if(self.param[key] == 'false') self.param[key]=false; + } + }, + + // Returns the standard Ajax request object used by the Jquery .ajax method + buildAjaxRequest: function(callbackSuccess) + { + var self = this; + + //prepare the ajax request + var ajaxRequest = + { + type: 'GET', + url: 'index.php', + dataType: 'html', + async: true, + error: ajaxHandleError, // Callback when the request fails + success: callbackSuccess, // Callback when the request succeeds + data: new Object + }; + + //Extract the configuration from the datatable and pass it to the API + for(var key in self.param) + { + if(typeof self.param[key] != "undefined") + ajaxRequest.data[key] = self.param[key]; + } + + return ajaxRequest; + }, + + // Function called to trigger the AJAX request + // The ajax request contains the function callback to trigger if the request is successful or failed + // displayLoading = false When we don't want to display the Loading... DIV #loadingDataTable + // for example when the script add a Loading... it self and doesn't want to display the generic Loading + reloadAjaxDataTable: function(displayLoading, callbackSuccess) + { + var self = this; + + if (typeof displayLoading == "undefined") + { + displayLoading = true; + } + if (typeof callbackSuccess == "undefined") + { + callbackSuccess = self.dataTableLoaded; + } + + if(displayLoading) + { + $('#'+self.workingDivId+' #loadingDataTable').css('display','block'); + } + + $.ajax(self.buildAjaxRequest(callbackSuccess)); + }, + + + // Function called when the AJAX request is successful + // it looks for the ID of the response and replace the very same ID + // in the current page with the AJAX response + dataTableLoaded: function(response) + { + var content = $(response); + var idToReplace = $(content).attr('id'); + var dataTableSel = $('#'+idToReplace); + + // if the current dataTable is situated inside another datatable + table = $(content).parents('table.dataTable'); + if(dataTableSel.parents('.dataTable').is('table')) + { + // we add class to the table so that we can give a different style to the subtable + $(content).children('table.dataTable').addClass('subDataTable'); + $(content).children('#dataTableFeatures').addClass('subDataTable'); + + //we force the initialisation of subdatatables + dataTableSel.html( $(content).html() ); + //dataTables[idToReplace].init( idToReplace, $('#'+idToReplace) ); + } + else + { + dataTableSel.html( $(content).html() ); + lazyScrollTo(dataTableSel[0], 400); + } + }, + + + /* This method is triggered when a new DIV is loaded, which happens + - at the first loading of the page + - after any AJAX loading of a DataTable + + This method basically add features to the DataTable, + - such as column sorting, searching in the rows, displaying Next / Previous links, etc. + - add styles to the cells and rows (odd / even styles) + - modify some rows to add images if a span img is found, or add a link if a span urlLink is found + or truncate the labels when they are too big + - bind new events onclick / hover / etc. to trigger AJAX requests, + nice hovertip boxes for truncated cells + */ + bindEventsAndApplyStyle: function(domElem) + { + var self = this; + + self.cleanParams(); + + self.handleSort(domElem); + + self.handleSearchBox(domElem); + self.handleLowPopulationLink(domElem); + self.handleOffsetInformation(domElem); + self.handleExportBox(domElem); + + self.applyCosmetics(domElem); + + self.handleSubDataTable(domElem); + }, + + // 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('<div id="sortIconContainer"><img id="sortIcon" width="'+imageSortWidth+'" height="'+imageSortHeight+'" src="themes/default/images/sort'+prefixSortIcon+ self.param.filter_sort_order+'.png" /></div>'); + } + }, + + // Add behaviour to the low population link + handleLowPopulationLink: function(domElem, callbackSuccess) + { + var self = this; + + // Showing the link "Exclude low population" for this DIV + if(self.param.show_exclude_low_population) + { + // Set the string for the DIV, either "Exclude low pop" or "Include all" + $('#dataTableExcludeLowPopulation', domElem) + .each( + function() + { + if(Number(self.param.filter_excludelowpop) != 0) + { + string = _pk_translate('CoreHome_IncludeAllPopulation'); + } + else + { + string = _pk_translate('CoreHome_ExcludeLowPopulation'); + } + $(this).html(string); + } + ) + // Bind a click event to the DIV that triggers the ajax request + .click( + function() + { + if(Number(self.param.filter_excludelowpop) != 0) + { + self.param.filter_excludelowpop = 0; + self.param.filter_excludelowpop_value = 0; + } + else + { + self.param.filter_excludelowpop = self.param.filter_excludelowpop_default; + self.param.filter_excludelowpop_value = self.param.filter_excludelowpop_value_default; + } + self.param.filter_offset = 0; + + self.reloadAjaxDataTable(true, callbackSuccess); + } + ); + } + + }, + + //behaviour for the DataTable 'search box' + handleSearchBox: function(domElem, callbackSuccess) + { + var self = this; + + // Showing the search box for dom element DIV and binding the event + // - on the keyword DIV anywhere, if the ENTER key is pressed + if(self.param.show_search) + { + var currentPattern = self.param.filter_pattern; + if(typeof self.param.filter_pattern != "undefined" + && self.param.filter_pattern.length > 0) + { + currentPattern = self.param.filter_pattern; + } + else if(typeof self.param.filter_pattern_recursive != "undefined" + && self.param.filter_pattern_recursive.length > 0) + { + currentPattern = self.param.filter_pattern_recursive; + } + else + { + currentPattern = ''; + } + + + $('#dataTableSearchPattern', domElem) + .show() + .each(function(){ + // when enter is pressed in the input field we submit the form + $('#keyword', this) + .keypress( + function(e) + { + if(submitOnEnter(e)) + { + $(this).siblings(':submit').submit(); + } + } + ) + .val(currentPattern) + ; + + $(':submit', this).submit( + function() + { + var keyword = $(this).siblings('#keyword').val(); + self.param.filter_offset = 0; + + if(self.param.search_recursive) + { + self.param.filter_column_recursive = 'label'; + self.param.filter_pattern_recursive = keyword; + } + else + { + self.param.filter_column = 'label'; + self.param.filter_pattern = keyword; + } + self.reloadAjaxDataTable(true, callbackSuccess); + } + ); + + $(':submit', this) + .click( function(){ $(this).submit(); }) + ; + + // in the case there is a searched keyword we display the RESET image + if(currentPattern) + { + var target = this; + var clearImg = $('<span style="position: relative;">\ + <img src="plugins/CoreHome/templates/images/reset_search.png" style="position: absolute; top: 4px; left: -15px; cursor: pointer; display: inline;" title="Clear"/>\ + </span>') + .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; + + // Showing the offset information (1 - 10 of 42) for this DIV + if( self.param.show_offset_information + // fix konqueror that doesnt recognize the show_offset_information false for the tag cloud + // and we really dont want to print Next/Previous for tag clouds + && self.param.viewDataTable != 'cloud' ) + { + $('#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'),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(); + } + ) + ; + + } + }, + + //behaviour for DataTable view box (data, table, cloud, graph, ...) + handleExportBox: function(domElem) + { + var self = this; + if( !self.param.idSubtable ) + { + // When the (+) image is hovered, the export buttons are displayed + $('#exportDataTableShow', domElem) + .show() + .hover( function() { + $(this).fadeOut('slow'); + $('#exportToFormat', $(this).parent()).show('slow'); + }, function(){} + ); + + //timeout object used to hide the datatable export buttons + var timeout = null; + + $('#exportDataTable', 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(){ + $('#exportToFormat', dom).fadeOut('fast', function(){ //queue the two actions + $('#exportDataTableShow', dom).show('fast');}); + }, 1000); + } + ); + + $('.viewDataTable', domElem).click( + function(){ + var viewDataTable = $(this).attr('format'); + self.resetAllFilters(); + self.param.viewDataTable = viewDataTable; + + self.reloadAjaxDataTable(); + } + ); + + $('#exportToFormat 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 str = '?module=API' + +'&method='+method + +'&format='+format + +'&idSite='+self.param.idSite + +'&period='+self.param.period + +'&date='+self.param.date; + if( filter_limit ) + { + str += '&filter_limit=' + filter_limit; + } + return str; + } + ); + } + }, + + //Apply some miscelleaneous style to the DataTable + applyCosmetics: function(domElem) + { + var self = this; + + // we truncate the labels columns from the second row + $("table tr td:first-child", domElem).truncate(30); + $('.truncated', domElem).Tooltip(); + + var imageLinkWidth = 10; + var imageLinkHeight = 9; + + // we add a link based on the <span id="urlLink"> present in the column label (the first column) + // if this span is there, we add the link around the HTML in the TD + // but we add this link only for the rows that are not clickable already (subDataTable) + $("tr:not('.subDataTable') td:first-child:has('#urlLink')", domElem).each( function(){ + + var imgToPrepend = ''; + if( $(this).find('img').length == 0 ) + { + imgToPrepend = '<img width="'+imageLinkWidth+'" height="'+imageLinkHeight+'" src="themes/default/images/link.gif" /> '; + } + var urlToLink = $('#urlLink',this).text(); + + $(this).html( + '<a target="_blank" href="' + urlToLink + '">' + imgToPrepend + $(this).html() + '</a>' + ); + }); + + + // 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'); + + // Change cursor on mouse hover if sort is enabled + if( self.param.enable_sort ) + { + $("th.sortable", domElem).hover( + function() + { + $(this).css({ cursor: "pointer"}); + }, + function() + { + $(this).css({ cursor: "auto"}); + } + ); + } + }, + + //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( '\ + <tr>\ + <td colspan="'+numberOfColumns+'" class="cellSubDataTable">\ + <div id="'+divIdToReplaceWithSubTable+'">\ + <span id="loadingDataTable" style="display:inline"><img src="themes/default/images/loading-blue.gif" />'+ _pk_translate('CoreHome_Loading') +'</span>\ + </div>\ + </td>\ + </tr>\ + '); + + var savedActionVariable = self.param.action; + + // reset all the filters from the Parent table + var filtersToRestore = self.resetAllFilters(); + + self.param.idSubtable = idSubTable; + self.param.action = self.param.actionToLoadTheSubTable; + self.reloadAjaxDataTable(false); + + self.param.action = savedActionVariable; + delete self.param.idSubtable; + self.restoreAllFilters(filtersToRestore); + + self.loadedSubDataTable[divIdToReplaceWithSubTable] = true; + + $(this).next().toggle(); + } + + $(this).next().toggle(); + } + ); + } +}; + + +// Helper function : +// returns true if the event keypress passed in parameter is the ENTER key +function submitOnEnter(e) +{ + var key=e.keyCode || e.which; + if (key==13) + { + return true; + } +} + + + + +//----------------------------------------------------------------------------- +// Action Data Table +//----------------------------------------------------------------------------- + +//inheritance declaration +//actionDataTable is a child of dataTable +actionDataTable.prototype = new dataTable; +actionDataTable.prototype.constructor = actionDataTable; + + +//A list of all our actionDataTables +//Test if the object have already been initialized (multiple includes) +if(typeof actionDataTables == "undefined") + var actionDataTables = new Object; + +//actionDataTable constructor +function actionDataTable() +{ + dataTable.call(this); + this.parentAttributeParent = ''; + this.parentId = ''; + this.disabledRowDom = new Object; //to handle double click on '+' row +} + +//Prototype of the actionDataTable object +actionDataTable.prototype = +{ + //method inheritance + cleanParams: dataTable.prototype.cleanParams, + reloadAjaxDataTable: dataTable.prototype.reloadAjaxDataTable, + buildAjaxRequest: dataTable.prototype.buildAjaxRequest, + handleLowPopulationLink: dataTable.prototype.handleLowPopulationLink, + handleSearchBox: dataTable.prototype.handleSearchBox, + + //initialisation of the actionDataTable + init: function(workingDivId, domElem) + { + if(typeof domElem == "undefined") + { + domElem = $('#'+workingDivId); + } + this.workingDivId = workingDivId; + this.bindEventsAndApplyStyle(domElem); + this.initialized = true; + }, + + //see dataTable::bindEventsAndApplyStyle + bindEventsAndApplyStyle: function(domElem) + { + var self = this; + + self.cleanParams(); + + // we dont display the link on the row with subDataTable when we are already + // printing all the subTables (case of recursive search when the content is + // including recursively all the subtables + if(!self.param.filter_pattern_recursive) + { + $('tr.subActionsDataTable.rowToProcess') + .click( function() + { + self.onClickActionSubDataTable(this) + }) + .hover(function() { + $(this).css({ cursor: "pointer"}); + }, + function() { + $(this).css({ cursor: "auto"}); + } + ); + } + + self.applyCosmetics(domElem); + + if( self.workingDivId != undefined) + { + self.handleSearchBox(domElem, self.actionsDataTableLoaded ); + self.handleLowPopulationLink(domElem, self.actionsDataTableLoaded ); + } + }, + + //see dataTable::applyCosmetics + applyCosmetics: function(domElem) + { + var self = this; + + $('tr.subActionsDataTable.rowToProcess') + .css('font-weight','bold'); + + $("th:first-child", domElem).addClass('label'); + + var imagePlusMinusWidth = 12; + var imagePlusMinusHeight = 12; + $('tr.subActionsDataTable.rowToProcess td:first-child') + .each( function(){ + $(this).prepend('<img width="'+imagePlusMinusWidth+'" height="'+imagePlusMinusHeight+'" class="plusMinus" src="" />'); + if(self.param.filter_pattern_recursive) + { + setImageMinus(this); + } + else + { + setImagePlus(this); + } + }); + + $('tr.rowToProcess') + .each( function() { + // we add the CSS style depending on the level of the current loading category + // we look at the style of the parent row + var style = $(this).prev().attr('class'); + var currentStyle = $(this).attr('class'); + + if( (typeof currentStyle != 'undefined') + && currentStyle.indexOf('level') >= 0 ) + { + } + else + { + var level = getNextLevelFromClass( style ); + $(this).addClass('level'+ level); + } + + // we add an attribute parent that contains the ID of all the parent categories + // this ID is used when collapsing a parent row, it searches for all children rows + // which 'parent' attribute's value contains the collapsed row ID + $(this).attr('parent', function(){ + return self.parentAttributeParent + ' ' + self.parentId; + } + ); + + // Add some styles on the cells even/odd + // label (first column of a data row) or not + $("td:first-child:odd", this).addClass('label labeleven'); + $("td:first-child:even", this).addClass('label labelodd'); + // we truncate the labels columns from the second row + $("td:first-child", this).truncate(30); + $('.truncated', this).Tooltip(); + }) + .removeClass('rowToProcess'); + }, + + // Called when the user click on an actionDataTable row + onClickActionSubDataTable: function(domElem) + { + var self = this; + + // get the idSubTable + var idSubTable = $(domElem).attr('id'); + + var divIdToReplaceWithSubTable = 'subDataTable_'+idSubTable; + + var NextStyle = $(domElem).next().attr('class'); + var CurrentStyle = $(domElem).attr('class'); + + var currentRowLevel = getLevelFromClass(CurrentStyle); + var nextRowLevel = getLevelFromClass(NextStyle); + + // if the row has not been clicked + // which is the same as saying that the next row level is equal or less than the current row + // because when we click a row the level of the next rows is higher (level2 row gives level3 rows) + if(currentRowLevel >= nextRowLevel) + { + //unbind click to avoid double click problem + $(domElem).unbind('click'); + self.disabledRowDom = $(domElem); + + var numberOfColumns = $(domElem).children().length; + $(domElem).after( '\ + <tr id="'+divIdToReplaceWithSubTable+'" class="cellSubDataTable">\ + <td colspan="'+numberOfColumns+'">\ + <span id="loadingDataTable" style="display:inline"><img src="themes/default/images/loading-blue.gif" /> Loading...</span>\ + </td>\ + </tr>\ + '); + var savedActionVariable = self.param.action; + + // reset search for subcategories + delete self.param.filter_column; + delete self.param.filter_pattern; + + self.param.idSubtable = idSubTable; + self.param.action = self.param.actionToLoadTheSubTable; + + self.reloadAjaxDataTable(false, function(resp){self.actionsSubDataTableLoaded(resp)}); + self.param.action = savedActionVariable; + delete self.param.idSubtable; + } + // else we toggle all these rows + else + { + var plusDetected = $('td img', domElem).attr('src').indexOf('plus') >= 0; + + $(domElem).siblings().each( function(){ + var parents = $(this).attr('parent'); + if(parents) + { + if(parents.indexOf(idSubTable) >= 0 || parents.indexOf('subDataTable_'+idSubTable) >= 0) + { + if(plusDetected) + { + $(this).css('display',''); + + //unroll everything and display '-' sign + //if the row is already opened + var NextStyle = $(this).next().attr('class'); + var CurrentStyle = $(this).attr('class'); + + var currentRowLevel = getLevelFromClass(CurrentStyle); + var nextRowLevel = getLevelFromClass(NextStyle); + + if(currentRowLevel < nextRowLevel) + setImageMinus(this); + } + else + { + $(this).css('display','none'); + } + } + } + }); + } + + // toggle the +/- image + var plusDetected = $('td img', domElem).attr('src').indexOf('plus') >= 0; + if(plusDetected) + { + setImageMinus(domElem); + } + else + { + setImagePlus(domElem); + } + }, + + //called when the full table actions is loaded + actionsDataTableLoaded: function(response) + { + var content = $(response); + var idToReplace = $(content).attr('id'); + + //reset parents id + self.parentAttributeParent = ''; + self.parentId = ''; + + var dataTableSel = $('#'+idToReplace); + dataTableSel.html($(content).html()); + lazyScrollTo(dataTableSel[0], 400); + }, + + // Called when a set of rows for a category of actions is loaded + actionsSubDataTableLoaded: function(response) + { + var self = this; + var idToReplace = $(response).attr('id'); + + // remove the first row of results which is only used to get the Id + var response = $(response).filter('tr').slice(1).addClass('rowToProcess'); + self.parentAttributeParent = $('tr#'+idToReplace).prev().attr('parent'); + self.parentId = idToReplace; + + $('tr#'+idToReplace).after( response ).remove(); + + + var re = /subDataTable_(\d+)/; + ok = re.exec(self.parentId); + if(ok) + { + self.parentId = ok[1]; + } + + // we execute the bindDataTableEvent function for the new DIV + self.init(self.workingDivId, $('#'+idToReplace)); + + //bind back the click event (disabled to avoid double-click problem) + self.disabledRowDom.click( + function() + { + self.onClickActionSubDataTable(this) + }); + } +}; + +//helper function for actionDataTable +function getLevelFromClass( style) +{ + if (typeof style == "undefined") return 0; + + var currentLevelIndex = style.indexOf('level'); + var currentLevel = 0; + if( currentLevelIndex >= 0) + { + currentLevel = Number(style.substr(currentLevelIndex+5,1)); + } + return currentLevel; +} + +//helper function for actionDataTable +function getNextLevelFromClass( style ) +{ + if (typeof style == "undefined") return 0; + currentLevel = getLevelFromClass(style); + newLevel = currentLevel; + // if this is not a row to process so + if( style.indexOf('rowToProcess') < 0 ) + { + newLevel = currentLevel + 1; + } + return newLevel; +} + +//helper function for actionDataTable +function setImageMinus( domElem ) +{ + $('img',domElem).attr('src', 'themes/default/images/minus.png'); +} + +//helper function for actionDataTable +function setImagePlus( domElem ) +{ + $('img',domElem).attr('src', 'themes/default/images/plus.png'); +} + diff --git a/plugins/CoreHome/templates/datatable.tpl b/plugins/CoreHome/templates/datatable.tpl new file mode 100644 index 0000000000..5cde2c9974 --- /dev/null +++ b/plugins/CoreHome/templates/datatable.tpl @@ -0,0 +1,40 @@ +<div id="{$id}" class="parentDiv">
+ {if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+ {else}
+ {if count($arrayDataTable) == 0}
+ <div id="emptyDatatable">{'CoreHome_TableNoData'|translate}</div>
+ {else}
+ <a name="{$id}"></a>
+ <table cellspacing="0" class="dataTable">
+ <thead>
+ <tr>
+ {foreach from=$dataTableColumns item=column}
+ <th class="sortable" id="{$column.id}"><div id="thDIV">{$column.displayName}</div></th>
+ {/foreach}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="subDataTable" id="{$row.idsubdatatable}"{/if}>
+ {foreach from=$dataTableColumns key=idColumn item=column}
+ <td>
+ {if $idColumn==0 && isset($row.metadata.url)}<span id="urlLink">{$row.metadata.url}</span>{/if}
+ {if $idColumn==0 && isset($row.metadata.logo)}<img {if isset($row.metadata.logoWidth)}width="{$row.metadata.logoWidth}"{/if} {if isset($row.metadata.logoHeight)}height="{$row.metadata.logoHeight}"{/if} src="{$row.metadata.logo}" />{/if}
+ {* sometimes all columns are not set in the datatable, we assume the value 0 *}
+ {if isset($row.columns[$column.name])}{$row.columns[$column.name]}{else}0{/if}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ {/if}
+
+ {if $showFooter}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+ {include file="CoreHome/templates/datatable_js.tpl"}
+ {/if}
+</div>
diff --git a/plugins/CoreHome/templates/datatable_actions.tpl b/plugins/CoreHome/templates/datatable_actions.tpl new file mode 100644 index 0000000000..275a6a2daa --- /dev/null +++ b/plugins/CoreHome/templates/datatable_actions.tpl @@ -0,0 +1,40 @@ +<div id="{$id}" class="parentDivActions">
+{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+{else}
+ {if count($arrayDataTable) == 0}
+ <div id="emptyDatatable">{'CoreHome_TableNoData'|translate}</div>
+ {else}
+ <table cellspacing="0" class="dataTable dataTableActions">
+ <thead>
+ <tr>
+ {foreach from=$dataTableColumns item=column}
+ <th class="sortable" id="{$column.id}">{$column.name}</td>
+ {/foreach}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="rowToProcess subActionsDataTable" id="{$row.idsubdatatable}"{else} class="actionsDataTable rowToProcess"{/if}>
+ {foreach from=$dataTableColumns key=idColumn item=column}
+ <td>
+ {* sometimes all columns are not set in the datatable, we assume the value 0 *}
+ {if isset($row.columns[$column.name])}{$row.columns[$column.name]}{else}0{/if}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+
+ </tfoot>
+ </table>
+ {/if}
+
+ {if $showFooter}
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {/if}
+ {include file="CoreHome/templates/datatable_actions_js.tpl"}
+
+ {/if}
+</div>
diff --git a/plugins/CoreHome/templates/datatable_actions_js.tpl b/plugins/CoreHome/templates/datatable_actions_js.tpl new file mode 100644 index 0000000000..5bf5ed2bca --- /dev/null +++ b/plugins/CoreHome/templates/datatable_actions_js.tpl @@ -0,0 +1,12 @@ + +<script type="text/javascript" defer="defer"> +$(document).ready(function(){literal}{{/literal} + actionDataTables['{$id}'] = new actionDataTable(); + actionDataTables['{$id}'].param = {literal}{{/literal} + {foreach from=$javascriptVariablesToSet key=name item=value name=loop} + {$name}: '{$value}'{if !$smarty.foreach.loop.last},{/if} + {/foreach} + {literal}};{/literal} + actionDataTables['{$id}'].init('{$id}'); +{literal}}{/literal}); +</script> diff --git a/plugins/CoreHome/templates/datatable_actions_recursive.tpl b/plugins/CoreHome/templates/datatable_actions_recursive.tpl new file mode 100644 index 0000000000..754f909158 --- /dev/null +++ b/plugins/CoreHome/templates/datatable_actions_recursive.tpl @@ -0,0 +1,44 @@ +<div id="{$id}" class="parentDivActions">
+{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+{else}
+ {if count($arrayDataTable) == 0}
+ <div id="emptyDatatable">{'CoreHome_TableNoData'|translate}</div>
+ {else}
+ <table cellspacing="0" class="dataTable dataTableActions">
+ <thead>
+ <tr>
+ {foreach from=$dataTableColumns item=column}
+ <th class="sortable" id="{$column.id}">{$column.displayName}</td>
+ {/foreach}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="level{$row.level} rowToProcess subActionsDataTable" id="{$row.idsubdatatable}"{else}class="actionsDataTable rowToProcess level{$row.level}"{/if}>
+ {foreach from=$dataTableColumns key=idColumn item=column}
+ <td>
+ {* sometimes all columns are not set in the datatable, we assume the value 0 *}
+ {if isset($row.columns[$column.name])}{$row.columns[$column.name]}{else}0{/if}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ </tbody>
+
+
+ </foot>
+ <tr><td colspan="{$dataTableColumns|@count}">
+
+ </td>
+ </tr>
+ </tfoot>
+ </table>
+ {/if}
+
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {include file="CoreHome/templates/datatable_actions_js.tpl"}
+
+ {/if}
+</div>
diff --git a/plugins/CoreHome/templates/datatable_actions_subdatable.tpl b/plugins/CoreHome/templates/datatable_actions_subdatable.tpl new file mode 100644 index 0000000000..08f91d2db2 --- /dev/null +++ b/plugins/CoreHome/templates/datatable_actions_subdatable.tpl @@ -0,0 +1,19 @@ +<tr id="{$id}"></tr>
+{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
+ {$arrayDataTable.message}
+{else}
+ {if count($arrayDataTable) == 0}
+ <tr><td colspan="{$nbColumns}">{'CoreHome_CategoryNoData'|translate}</td></tr>
+ {else}
+ {foreach from=$arrayDataTable item=row}
+ <tr {if $row.idsubdatatable}class="subActionsDataTable" id="{$row.idsubdatatable}"{else}class="actionsDataTable"{/if}>
+ {foreach from=$dataTableColumns key=idColumn item=column}
+ <td>
+ {* sometimes all columns are not set in the datatable, we assume the value 0 *}
+ {if isset($row.columns[$column.name])}{$row.columns[$column.name]}{else}0{/if}
+ </td>
+ {/foreach}
+ </tr>
+ {/foreach}
+ {/if}
+{/if}
diff --git a/plugins/CoreHome/templates/datatable_footer.tpl b/plugins/CoreHome/templates/datatable_footer.tpl new file mode 100644 index 0000000000..f1e532eaf2 --- /dev/null +++ b/plugins/CoreHome/templates/datatable_footer.tpl @@ -0,0 +1,36 @@ +<div id="dataTableFeatures">
+ <span id="dataTableExcludeLowPopulation"></span>
+
+ <span id="dataTableSearchPattern">
+ <input id="keyword" type="text" length="15" />
+ <input type="submit" value="{'General_Search'|translate}" />
+ </span>
+
+ <span id="dataTablePages"></span>
+ <span id="dataTablePrevious">< {'General_Previous'|translate}</span>
+ <span id="dataTableNext">{'General_Next'|translate} ></span>
+ <div>
+ <span id="exportDataTable">
+ <span id="exportToFormat" style="display:none">
+ <img width="16" height="16" src="themes/default/images/export.png" />
+ <span id="linksExportToFormat" style="display:none">
+ <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="CSV" filter_limit="100">CSV</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="XML" filter_limit="100">XML</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="JSON" filter_limit="100">Json</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="PHP" filter_limit="100">Php</a>
+ </span>
+ <a class="viewDataTable" format="table"><img width="16" height="16" src="themes/default/images/table.png" title="{'General_Table'|translate}" /></a>
+ <a class="viewDataTable" format="cloud"><img width="16" height="16" src="themes/default/images/tagcloud.png" title="{'General_TagCloud'|translate}" /></a>
+ <a class="viewDataTable" format="graphVerticalBar"><img width="16" height="16" src="themes/default/images/chart_bar.png" title="{'General_VBarGraph'|translate}" /></a>
+ <a class="viewDataTable" format="graphPie"><img width="16" height="16" src="themes/default/images/chart_pie.png" title="{'General_Piechart'|translate}" /></a>
+ </span>
+ <span id="exportDataTableShow" style="display:none">
+ <img src="plugins/CoreHome/templates/images/more.png" />
+ </span>
+ </span>
+
+ <span id="loadingDataTable"><img src="themes/default/images/loading-blue.gif" /> {'General_LoadingData'|translate}</span>
+ </div>
+</div>
+
+<div class="dataTableSpacer" />
diff --git a/plugins/CoreHome/templates/datatable_js.tpl b/plugins/CoreHome/templates/datatable_js.tpl new file mode 100644 index 0000000000..83043606f4 --- /dev/null +++ b/plugins/CoreHome/templates/datatable_js.tpl @@ -0,0 +1,12 @@ + +<script type="text/javascript" defer="defer"> +$(document).ready(function(){literal}{{/literal} + dataTables['{$id}'] = new dataTable(); + dataTables['{$id}'].param = {literal}{{/literal} + {foreach from=$javascriptVariablesToSet key=name item=value name=loop} + {$name}: '{$value}'{if !$smarty.foreach.loop.last},{/if} + {/foreach} + {literal}};{/literal} + dataTables['{$id}'].init('{$id}'); +{literal}}{/literal}); +</script> diff --git a/plugins/CoreHome/templates/date.js b/plugins/CoreHome/templates/date.js new file mode 100644 index 0000000000..2ee73f08bf --- /dev/null +++ b/plugins/CoreHome/templates/date.js @@ -0,0 +1,89 @@ +$(document).ready(function(){ + $("#periodString").hide(); + $("#otherPeriods").hide(); + $("#calendar").hide(); + $("#periodString").show(); + + // we get the content of the div before modifying it (append image, etc.) + // so we can restore its value when we want + var savedCurrentPeriod = $("#periodString #currentPeriod").html(); + + // timeout used to fadeout the menu + var timeout = null; + var timeoutLength; + + // restore the normal style of the current period type eg "DAY" + function restoreCurrentPeriod() + { + $("#currentPeriod") + .removeClass("hoverPeriod") + .html(savedCurrentPeriod); + } + // remove the sub menu created that contains the other periods availble + // eg. week | month | year + function removePeriodMenu() { + $("#otherPeriods").fadeOut('fast'); + setCurrentPeriodStyle = true; + } + + // state machine a bit complex and was hard to come up with + // there should be a simpler way to do it with jquery... + // if set to true, means that we want to style our current period + // for example add bold and append the image + var setCurrentPeriodStyle = true; + + $("#periodString #periods") + .hover(function(){ + $(this).css({ cursor: "pointer"}); + + // cancel the timeout + // indeed if the user goes away of the div and goes back on + // we don't hide the submenu! + if(timeout != null) + { + clearTimeout(timeout); + timeout = null; + timeoutLength = 500; + } + else + { + timeoutLength = 0; + setCurrentPeriodStyle = true; + } + if( setCurrentPeriodStyle == true) + { + $("#currentPeriod:not(.hoverPeriod)") + .addClass("hoverPeriod") + .append(' <img src="plugins/CoreHome/templates/images/more_period.gif" style="vertical-align:middle">'); + } + }, 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(){ + $("#calendar").toggle(); + }); +} );
\ No newline at end of file diff --git a/plugins/CoreHome/templates/graph.tpl b/plugins/CoreHome/templates/graph.tpl new file mode 100644 index 0000000000..90a4d8fe63 --- /dev/null +++ b/plugins/CoreHome/templates/graph.tpl @@ -0,0 +1,16 @@ +<div id="{$id}" class="{if $graphType=='evolution'}parentDivGraphEvolution{else}parentDivGraph{/if}">
+{$jsInvocationTag}
+
+{if $showFooter}
+ <br/><br/>
+ <form class="formEmbedCode" id="{$formId}">
+ Embed <input name="embed_code" value="{$codeEmbed}" onclick="javascript:document.getElementById('{$formId}').embed_code.focus();document.getElementById('{$formId}').embed_code.select();" readonly="true" type="text">
+
+ | <a target="_blank" href="{$urlData}">{'General_GraphData'|translate}</a>
+ </form>
+
+ {include file="CoreHome/templates/datatable_footer.tpl"}
+ {include file="CoreHome/templates/datatable_js.tpl"}
+{/if}
+
+</div>
diff --git a/plugins/CoreHome/templates/header.tpl b/plugins/CoreHome/templates/header.tpl new file mode 100644 index 0000000000..0af51cce87 --- /dev/null +++ b/plugins/CoreHome/templates/header.tpl @@ -0,0 +1,8 @@ + +<span id="righttext"> + <span id="messageToUsers"><a href='http://piwik.org'>Piwik</a> is a collaborative project and still Beta. <br /> + If you want to help, please <u><a href="mailto:hello@piwik.org?subject=Piwik">contact us</a></u>.</span> +</span> + +{include file="CoreHome/templates/logo.tpl"} +{include file="CoreHome/templates/period_select.tpl"}
\ No newline at end of file diff --git a/plugins/CoreHome/templates/images/bg_header.jpg b/plugins/CoreHome/templates/images/bg_header.jpg Binary files differnew file mode 100644 index 0000000000..1733b0e355 --- /dev/null +++ b/plugins/CoreHome/templates/images/bg_header.jpg diff --git a/plugins/CoreHome/templates/images/bullet1.gif b/plugins/CoreHome/templates/images/bullet1.gif Binary files differnew file mode 100644 index 0000000000..d4ed73eea7 --- /dev/null +++ b/plugins/CoreHome/templates/images/bullet1.gif diff --git a/plugins/CoreHome/templates/images/bullet2.gif b/plugins/CoreHome/templates/images/bullet2.gif Binary files differnew file mode 100644 index 0000000000..d51d938dd1 --- /dev/null +++ b/plugins/CoreHome/templates/images/bullet2.gif diff --git a/plugins/CoreHome/templates/images/more.png b/plugins/CoreHome/templates/images/more.png Binary files differnew file mode 100644 index 0000000000..cba03a05f5 --- /dev/null +++ b/plugins/CoreHome/templates/images/more.png diff --git a/plugins/CoreHome/templates/images/more_date.gif b/plugins/CoreHome/templates/images/more_date.gif Binary files differnew file mode 100644 index 0000000000..87da6b4963 --- /dev/null +++ b/plugins/CoreHome/templates/images/more_date.gif diff --git a/plugins/CoreHome/templates/images/more_period.gif b/plugins/CoreHome/templates/images/more_period.gif Binary files differnew file mode 100644 index 0000000000..b0c9787851 --- /dev/null +++ b/plugins/CoreHome/templates/images/more_period.gif diff --git a/plugins/CoreHome/templates/images/reset_search.png b/plugins/CoreHome/templates/images/reset_search.png Binary files differnew file mode 100644 index 0000000000..cb1d9e80a2 --- /dev/null +++ b/plugins/CoreHome/templates/images/reset_search.png diff --git a/plugins/CoreHome/templates/images/search.png b/plugins/CoreHome/templates/images/search.png Binary files differnew file mode 100644 index 0000000000..6d1f037c69 --- /dev/null +++ b/plugins/CoreHome/templates/images/search.png diff --git a/plugins/CoreHome/templates/index.tpl b/plugins/CoreHome/templates/index.tpl new file mode 100644 index 0000000000..43fb78a0d8 --- /dev/null +++ b/plugins/CoreHome/templates/index.tpl @@ -0,0 +1,77 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+<title>Piwik - Your Web Analytics Reports</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+{loadJavascriptTranslations modules='CoreHome'}
+
+<script type="text/javascript">
+var period = "{$period}";
+var currentDateStr = "{$date}";
+var minDateYear = {$minDateYear};
+var minDateMonth = {$minDateMonth};
+var minDateDay = {$minDateDay};
+</script>
+
+<script type="text/javascript" src="libs/jquery/jquery.js"></script>
+
+<script type="text/javascript" src="themes/default/common.js"></script>
+<script type="text/javascript" src="libs/jquery/jquery.dimensions.js"></script>
+<script type="text/javascript" src="libs/jquery/tooltip/jquery.tooltip.js"></script>
+<script type="text/javascript" src="libs/jquery/truncate/jquery.truncate.js"></script>
+<script type="text/javascript" src="libs/jquery/jquery.scrollTo.js"></script>
+<script type="text/javascript" src="libs/jquery/jquery-calendar.js"></script>
+<script type="text/javascript" src="libs/swfobject/swfobject.js"></script>
+
+<script type="text/javascript" src="plugins/CoreHome/templates/datatable.js"></script>
+<script type="text/javascript" src="plugins/CoreHome/templates/calendar.js"></script>
+<script type="text/javascript" src="plugins/CoreHome/templates/date.js"></script>
+
+<script type="text/javascript" src="libs/jquery/jquery.blockUI.js"></script>
+<script type="text/javascript" src="libs/jquery/ui.mouse.js"></script>
+<script type="text/javascript" src="libs/jquery/ui.sortable_modif.js"></script>
+
+<link rel="stylesheet" href="plugins/CoreHome/templates/datatable.css" />
+<link rel="stylesheet" href="plugins/Dashboard/templates/dashboard.css" />
+<style type="text/css">@import url(libs/jquery/jquery-calendar.css);</style>
+
+<script type="text/javascript" src="libs/jquery/superfish_modified.js"></script>
+<script type="text/javascript" src="plugins/CoreHome/templates/menu.js"></script>
+<link rel="stylesheet" type="text/css" href="plugins/CoreHome/templates/menu.css" media="screen" />
+<link rel="stylesheet" type="text/css" href="plugins/CoreHome/templates/style.css" media="screen" />
+
+<script type="text/javascript" src="libs/jquery/thickbox.js"></script>
+<link rel="stylesheet" href="libs/jquery/thickbox.css" />
+</head>
+<body>
+
+{include file="CoreHome/templates/top_bar.tpl"}
+
+<br clear="all" />
+
+<div id="header">
+{include file="CoreHome/templates/header.tpl"}
+</div>
+
+<noscript>
+<span id="javascriptDisable">
+{'CoreHome_JavascriptDisabled'|translate:'<a href="">':'</a>'}
+</span>
+</noscript>
+
+<br />
+{include file="CoreHome/templates/menu.tpl"}
+
+<div style='clear:both'></div>
+
+<div id="loadingPiwik" {if $basicHtmlView}style="display:none"{/if}><img src="themes/default/images/loading-blue.gif" alt="" /> {'General_LoadingData'|translate}</div>
+<div id="loadingError">{'General_ErrorRequest'|translate}</div>
+
+<div id='content'>
+{if $content}{$content}{/if}
+</div>
+
+{include file="CoreHome/templates/piwik_tag.tpl"}
+</body>
+</html>
diff --git a/plugins/CoreHome/templates/logo.tpl b/plugins/CoreHome/templates/logo.tpl new file mode 100644 index 0000000000..ce2656865e --- /dev/null +++ b/plugins/CoreHome/templates/logo.tpl @@ -0,0 +1,14 @@ +<span id="logo"> +<a href="http://piwik.org" target="_blank" title="open source web analytics" style="text-decoration: none;"> + <span style="color: rgb(245, 223, 114);">P</span><span style="color: rgb(241, 175, 108);">i</span><span style="color: rgb(241, 117, 117);">w</span><span style="color: rgb(155, 106, 58);">i</span><span style="color: rgb(107, 50, 11);">k</span> + <!-- + <span style="font-size: 20pt; letter-spacing: -1pt;"> + <span style="color: rgb(107, 50, 11);">#</span> + <span style="color: rgb(107, 50, 11);"> Open</span> + <span style="color: rgb(107, 50, 11);"> source </span> + <span style="color: rgb(107, 50, 11);">web </span> + <span style="color: rgb(107, 50, 11);">analytics</span> + </span> + --> +</a> +</span>
\ No newline at end of file diff --git a/plugins/CoreHome/templates/menu.css b/plugins/CoreHome/templates/menu.css new file mode 100644 index 0000000000..00baa61df0 --- /dev/null +++ b/plugins/CoreHome/templates/menu.css @@ -0,0 +1,111 @@ +.nav,.nav * { + margin: 0; + padding: 0; +} + +.nav { + padding-bottom: 2.5em; + height: 2.5em; + float: left; + line-height: 1.0; + margin-bottom: 1.5em; + position: relative; +} + +.nav ul { + background: #fff; /*IE6 needs this*/ + float: left; + position: relative; +} + +/* LEVEL1 NORMAL */ +.nav li { + background: #DFE6FF; + border-left: 1px solid #fff; + float: left; + list-style: none; + z-index: 49; +} + +.nav li.current ul { + z-index: 49; +} + +.nav li.sfHover ul,ul.nav li:hover ul { + z-index: 50; +} + +/* LEVEL2 NORMAL */ +.nav li li { + background: #FBFFFF; + border-left-color: #AABDE6; +} + +.nav a { + border-bottom: 1px solid #CFDEFF; + color: #13a; + display: block; + float: left; + padding: .75em 0 .75em 1em; + text-decoration: none; + width: 8em; +} + +.nav li ul { + left: 0; + top: -999em; + position: absolute; + width: 48em; +} + +/* LEVEL1 HOVER */ +.nav li:hover,.nav li.sfHover,.nav li.current,.nav a:focus,.nav a:hover,.nav a:active + { + background: #C9D5FF; +} + +.nav li { + font-weight: normal; +} + +.nav li.sfHover { + font-weight: bold; +} + +/* LEVEL2 HOVER */ +.nav li li:hover,.nav li li.sfHover,.nav li li a:focus,.nav li li a:hover,.nav li li a:active + { + background: #C9F6FF; + font-weight: bold; +} + +.nav li.sfHover a,.nav li.current a,.nav a:focus,.nav a:hover,.nav a:active + { + border-bottom: none; +} + +.nav li li.current a { + font-weight: bold; +} + +.nav li:hover ul, /* pure CSS hover is removed below */ body .nav li.current ul, + /* this must be more specific than the .superfish override below */ ul.nav li.sfHover ul + { + top: 2.5em; +} + +.nav li:hover li ul,.nav li.sfHover li ul { + top: -999em; +} + +.nav li li:hover ul, /* pure CSS hover is removed below */ ul.nav li li.sfHover ul + { + top: 2.5em; +} + +/*following rule negates pure CSS hovers +so submenu remains hidden and JS controls +when and how it appears*/ +.superfish li:hover ul,.superfish li li:hover ul { + top: -999em; +}
\ No newline at end of file diff --git a/plugins/CoreHome/templates/menu.js b/plugins/CoreHome/templates/menu.js new file mode 100644 index 0000000000..c87d894a05 --- /dev/null +++ b/plugins/CoreHome/templates/menu.js @@ -0,0 +1,126 @@ +// menu constructor +function menu() +{ + this.param = new Object; +} + +// this should be in the menu prototype but I couldn't figure out +// how to use it as a callback in the jquery ajax request + + +//Prototype of the DataTable object +menu.prototype = +{ + menuSectionLoaded: function (content, urlLoaded) + { + if(urlLoaded == menu.prototype.lastUrlRequested) + { + $('#content').html( content ).show(); + $('#loadingPiwik').hide(); + menu.prototype.lastUrlRequested = null; + //console.log('display '+urlLoaded); + } + else + { + //console.log('loaded '+urlLoaded+' but expecting to display '+menu.prototype.lastUrlRequested); + } + }, + + customAjaxHandleError: function () + { + menu.prototype.lastUrlRequested = null; + ajaxHandleError(); + }, + + overMainLI: function () + { + $(this).siblings().removeClass('sfHover'); + }, + + outMainLI: function () + { + }, + + onClickLI: function () + { + var self = this; + var urlAjax = $('a',this).attr('name'); + function menuSectionLoaded(content) + { + menu.prototype.menuSectionLoaded(content, urlAjax); + } + + // showing loading... + $('#loadingPiwik').show(); + $('#content').hide(); + + if(menu.prototype.lastUrlRequested == urlAjax) + { + return false; + } + menu.prototype.lastUrlRequested = urlAjax; + + // we are in the SUB UL LI + if($(this).find('ul li').size() == 0) + { + // console.log('clicked SUB LI'); + $(this).addClass('sfHover'); + } + // we clicked on a MAIN LI + else + { + $(this).find('>ul li:first').addClass('sfHover'); + } + + //prepare the ajax request + ajaxRequest = + { + type: 'GET', + url: urlAjax, + dataType: 'html', + async: true, + error: menu.prototype.customAjaxHandleError, // Callback when the request fails + success: menuSectionLoaded, // Callback when the request succeeds + data: new Object + }; + $.ajax(ajaxRequest); + + return false; + + }, + + init: function() + { + var self = this; + this.param.superfish = $('.nav') + .superfish({ + pathClass : 'current', + animation : {opacity:'show'}, + delay : 1000 + }); + this.param.superfish.find("li") + .click( self.onClickLI ) + ; + + this.param.superfish + .find("li:has(ul)") + .hover(self.overMainLI, self.outMainLI) + ; + }, + + loadFirstSection: function() + { + var self=this; + $('li:first', self.param.superfish) + .click() + .each(function(){ + $(this).showSuperfishUl(); + }); + } +} + +$(document).ready( function(){ + piwikMenu = new menu(); + piwikMenu.init(); + piwikMenu.loadFirstSection(); +});
\ No newline at end of file diff --git a/plugins/CoreHome/templates/menu.tpl b/plugins/CoreHome/templates/menu.tpl new file mode 100644 index 0000000000..d7f036cbc3 --- /dev/null +++ b/plugins/CoreHome/templates/menu.tpl @@ -0,0 +1,16 @@ + +<ul class="nav"> +{foreach from=$menu key=level1 item=level2 name=menu} +<li> + <a name='{$level2._url|@urlRewriteWithParameters}' href='{$level2._url|@urlRewriteBasicView}'>{$level1} ↓</a> + <ul> + {foreach from=$level2 key=name item=urlParameters name=level2} + {if $name != '_url'} + <li><a name='{$urlParameters|@urlRewriteWithParameters}' href='{$urlParameters|@urlRewriteBasicView}'>{$name}</a></li> + {/if} + {/foreach} + </ul> +</li> +{/foreach} +</ul> + diff --git a/plugins/CoreHome/templates/period_select.tpl b/plugins/CoreHome/templates/period_select.tpl new file mode 100644 index 0000000000..f836dc449b --- /dev/null +++ b/plugins/CoreHome/templates/period_select.tpl @@ -0,0 +1,13 @@ +<span id="periodString">
+ <span id="date"><img src='plugins/CoreHome/templates/images/more_date.gif' style="vertical-align:middle" alt="" /> {$prettyDate}</span> -
+ <span id="periods">
+ <span id="currentPeriod">{$periodsNames.$period}</span>
+ <span id="otherPeriods">
+ {foreach from=$otherPeriods item=thisPeriod} | <a href='{url period=$thisPeriod}'>{$periodsNames.$thisPeriod}</a>{/foreach}
+ </span>
+ </span>
+ <br/>
+ <span id="calendar"></span>
+</span>
+<div style="clear:both"></div>
+
diff --git a/plugins/CoreHome/templates/piwik_tag.tpl b/plugins/CoreHome/templates/piwik_tag.tpl new file mode 100644 index 0000000000..3f207f6bda --- /dev/null +++ b/plugins/CoreHome/templates/piwik_tag.tpl @@ -0,0 +1,19 @@ +{if ereg('http://127.0.0.1|http://localhost|http://piwik.org', $url)} +{literal} +<!-- Piwik --> +<a href="http://piwik.org" title="Web analytics" onclick="window.open(this.href);return(false);"> +<script language="javascript" src="piwik.js" type="text/javascript"></script> +<script type="text/javascript"> +<!-- +piwik_action_name = ''; +piwik_idsite = 1; +piwik_url = 'piwik.php'; +piwik_vars = { 'video_play':1, 'video_finished':0 }; +piwik_log(piwik_action_name, piwik_idsite, piwik_url, piwik_vars); +//--> +</script><object> +<noscript><p>Web analytics <img src="piwik.php" style="border:0" alt="piwik"/></p> +</noscript></object></a> +<!-- /Piwik --> +{/literal} +{/if}
\ No newline at end of file diff --git a/plugins/CoreHome/templates/sites_select.tpl b/plugins/CoreHome/templates/sites_select.tpl new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/plugins/CoreHome/templates/sites_select.tpl diff --git a/plugins/CoreHome/templates/sparkline.js b/plugins/CoreHome/templates/sparkline.js new file mode 100644 index 0000000000..2ab68d18c7 --- /dev/null +++ b/plugins/CoreHome/templates/sparkline.js @@ -0,0 +1,59 @@ + +$(document).ready( function(){ + + //for every section + $("a[name='evolutionGraph']").each( + function() + { + //try to find the graph + var graph = $(this); + + if(graph && graph.size() > 0) + { + //console.log($(this).parent()); + //try to find sparklines and add them clickable behaviour + $(this).parent().find('p').each( + function() + { + var url = ""; + //find the sparkline and get it's src attribute + $(".sparkline", this).each( + function() + { + //search viewDataTable parameter and replace it with value for chart + var reg = new RegExp("(viewDataTable=sparkline)", "g"); + url = this.src.replace(reg,'viewDataTable=generateDataChartEvolution'); + } + ); + + if(url != "") + { + $("*", this).each( + function() + { + //on click, reload the graph with the new url + $(this).click( + function() + { + //get the main page graph and reload with new data + findSWFGraph(graph.attr('graphId')+"Chart_swf").reload(url); + lazyScrollTo(graph[0], 400); + } + ); + + //on hover, change cursor to indicate clickable item + $(this).hover( + function() + { + $(this).css({ cursor: "pointer"}); + }, function (){} + ); + } + ); + } + } + ); + } + } + ); +}); diff --git a/plugins/CoreHome/templates/style.css b/plugins/CoreHome/templates/style.css new file mode 100644 index 0000000000..c7ab9408ad --- /dev/null +++ b/plugins/CoreHome/templates/style.css @@ -0,0 +1,258 @@ +/* reset style */ +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td + { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} + +body { + line-height: 1.33; + background-color: white; + color: black; +} + +ol,ul { + list-style: none; +} + +blockquote,q { + quotes: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} + +del { + text-decoration: line-through; +} + +/* start piwik styles */ +body { + font-family: Georgia, "Trebuchet MS", Verdana, Arial, Helvetica, + sans-serif; + font-size: 1em; +} + +#title { + align: left; +} + +h1 { + font-size: 2em; + color: #0F1B2E; + padding-bottom: 1em; +} + +h2 { + font-size: 1.6em; + color: #1D3256; + padding-bottom: 0.5em; +} + +h3 { + font-size: 1.3em; + margin-top: 2em; + color: #1D3256; +} + +a { + color: #0F1B2E; +} + +p { + padding-bottom: 1em; + margin-right: 1em; +} + +/* Content */ +#content { + margin-left: 10px; +} + +/* 2 columns reports */ +#leftcolumn { + float: left; + width: 45%; + padding-left: 10px; + padding-right: 20px; +} + +#rightcolumn { + float: right; + width: 45%; + padding-left: 20px; +} + +#header { + line-height: 1; +} + +#logo { + float: left; + display: inline; + font-size: 45pt; + padding-left: 15px; + padding-left: 15px; +} + +#righttext { + position: relative; + text-align: left; + float: right; + margin-top: 10px; + font-size: 1em; + width: 300px; + line-height: 1.33; +} + +#calendar { + display: block; +} + +#periodString #date,#periodString #periods,#periodString #date a,#periodString #periods a + { + color: #520202; + font-size: 15pt; +} + +#periodString { + padding-top: 2em; + margin-left: 350px; + display: block; +} + +/* Calendar*/ +.calendar td.dateToday,.calendar td.dateToday a { + font-weight: bold; +} + +.calendar td.dateUsedStats,.calendar td.dateUsedStats a { + color: #2E85FF; + border-color: #2E85FF; +} + +.calendar td.calendar_unselectable { + color: #F2F7FF; +} + +.calendar { + line-height: 1.33; +} + +#otherPeriods a { + text-decoration: none; +} + +#otherPeriods a:hover { + text-decoration: underline; +} + +#currentPeriod { + border-bottom: 1px dotted #520202; +} + +.hoverPeriod { + cursor: pointer; + font-weight: bold; + border-bottom: 1px solid #520202; +} + +/* OTHER */ +#loadingError { + font-weight: bold; + font-size: 1.1em; + color: #F88D22; + padding: 0.5em; + margin-left: 30%; + display: none; +} + +#loadingPiwik { + font-weight: bold; + font-size: 1.1em; + color: #193B6C; + padding: 0.5em; + margin-left: 30%; +} + +#bar,#user { + font-family: arial, sans-serif !important; + height: 20px; + padding: 4px 10px 0px !important; + background-color: #FFFFFF; + font-size: 13px; + padding-top: 4px !important; +} + +#bar { + float: left; + height: 22px; + padding-left: 10px; +} + +.bar-elem { + height: 22px; + margin-right: 0.73em; + vertical-align: top; + color: #000000; + margin-bottom: 0px; +} + +.line { + left: 0pt; + position: absolute; + top: 30px; + width: 100%; + border-top: 1px solid #C9D7F1; + font-size: 0pt; + height: 0pt; +} + +#periodSelection,#periodSelection a { + color: #8D92AA; +} + +#messageToUsers,#messageToUsers a { + color: red; + font-size: 0.9em; + text-decoration: none; + width: 100%; +} + +.sparkline { + vertical-align: middle; + padding-right: 10px; +} + +/* top right bar */ +#loggued { + float: right; + text-align: right; + margin-right: 20px; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 5px; + border-bottom: 1px dotted #E2E3FE; + border-left: 1px dotted #E2E3FE; +} + +#loggued form { + display: inline; +} + +#javascriptDisable,#javascriptDisable a { + font-weight: bold; + color: #F88D22; +}
\ No newline at end of file diff --git a/plugins/CoreHome/templates/top_bar.tpl b/plugins/CoreHome/templates/top_bar.tpl new file mode 100644 index 0000000000..7627d6b745 --- /dev/null +++ b/plugins/CoreHome/templates/top_bar.tpl @@ -0,0 +1,31 @@ +<div id="bar">
+<span class="bar-elem"><b>Your Dashboard</b></span>
+<span class="bar-elem"><a href='?module=CoreAdminHome&action=showInContext&moduleToLoad=API&actionToLoad=listAllAPI&module=CoreAdminHome&action=showInContext'>API</a></span>
+<span class="bar-elem"><a href='?module=Widgetize'>Widgets</a></span>
+<span class="bar-elem"><a href='?module=Feedback&action=index&keepThis=true&TB_iframe=true&height=400&width=320' title="Send us feedback" class="thickbox">Send us feedback</a></span>
+</div>
+
+
+<div align="right">
+<div id="user" align="right" width="100%" style="padding: 0pt 0pt 4px; font-size: 84%;">
+<nobr>
+<form action="{url idSite=null}" method="get" id="siteSelection">
+<small>
+ <strong>{$userLogin}</strong>
+ |
+<a href='?module=CoreAdminHome'>Admin</a> |
+<span id="sitesSelection">Site <select name="idSite" onchange='javascript:this.form.submit()'>
+ <optgroup label="Sites">
+ {foreach from=$sites item=info}
+ <option label="{$info.name}" value="{$info.idsite}" {if $idSite==$info.idsite} selected="selected"{/if}>{$info.name}</option>
+ {/foreach}
+ </optgroup>
+</select>
+{hiddenurl idSite=null}
+</span> | {if $userLogin=='anonymous'}<a href='?module=Login'>{'Login_LogIn'|translate}</a>{else}<a href='?module=Login&action=logout'>{'Login_Logout'|translate}</a>{/if}
+</small>
+</form>
+</nobr>
+</div>
+</div>
+
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php new file mode 100644 index 0000000000..2e3a673671 --- /dev/null +++ b/plugins/CorePluginsAdmin/Controller.php @@ -0,0 +1,65 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id: Controller.php 169 2008-01-14 05:41:15Z matt $ + * + * @package Piwik_ExamplePlugin + */ + + + +/** + * + * @package Piwik_ExamplePlugin + */ +class Piwik_CorePluginsAdmin_Controller extends Piwik_Controller +{ + function index() + { + Piwik::checkUserIsSuperUser(); + + $listPlugins = Piwik_PluginsManager::getInstance()->readPluginsDirectory(); + + $loadedPlugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins(); + $plugins = array(); + + foreach($listPlugins as $pluginName) + { + $oPlugin = Piwik_PluginsManager::getInstance()->loadPlugin($pluginName); + $plugins[$pluginName]= array( 'activated' => Piwik_PluginsManager::getInstance()->isPluginEnabled($pluginName), + 'alwaysActivated' => Piwik_PluginsManager::getInstance()->isPluginAlwaysActivated($pluginName), + 'info' => $oPlugin->getInformation() + ); + } + + $view = new Piwik_View('CorePluginsAdmin/templates/manage.tpl'); + + $view->pluginsName = $plugins; + + echo $view->render(); + } + + function deactivate() + { + Piwik::checkUserIsSuperUser(); + + $pluginName = Piwik_Common::getRequestVar('pluginName', null, 'string'); + Piwik_PluginsManager::getInstance()->deactivatePlugin($pluginName); + + Piwik_Url::redirectToUrl('index.php?module=CoreAdminHome&action=showInContext&moduleToLoad=CorePluginsAdmin'); + + } + function activate() + { + Piwik::checkUserIsSuperUser(); + + $pluginName = Piwik_Common::getRequestVar('pluginName', null, 'string'); + Piwik_PluginsManager::getInstance()->activatePlugin($pluginName); + + Piwik_Url::redirectToUrl('index.php?module=CoreAdminHome&action=showInContext&moduleToLoad=CorePluginsAdmin'); + + } +} diff --git a/plugins/CorePluginsAdmin/CorePluginsAdmin.php b/plugins/CorePluginsAdmin/CorePluginsAdmin.php new file mode 100644 index 0000000000..dc301c29fe --- /dev/null +++ b/plugins/CorePluginsAdmin/CorePluginsAdmin.php @@ -0,0 +1,32 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id: ExamplePlugin.php 169 2008-01-14 05:41:15Z matt $ + * + * @package Piwik_CorePluginsAdmin + */ + +class Piwik_CorePluginsAdmin extends Piwik_Plugin +{ + public function getInformation() + { + return array( + // name must be the className prefix! + 'name' => 'CorePluginsAdmin', + 'description' => 'Plugins Administration Interface.', + 'author' => 'Piwik', + 'homepage' => 'http://piwik.org/', + 'version' => '0.1', + ); + } + + function postLoad() + { + Piwik_AddAdminMenu(Piwik_Translate('CorePluginsAdmin_MenuPlugins'), array('module' => 'CorePluginsAdmin')); + } +} + + diff --git a/plugins/CorePluginsAdmin/templates/manage.tpl b/plugins/CorePluginsAdmin/templates/manage.tpl new file mode 100644 index 0000000000..11c0be2c01 --- /dev/null +++ b/plugins/CorePluginsAdmin/templates/manage.tpl @@ -0,0 +1,135 @@ +
+{literal}
+<style>
+.widefat {
+ border-width: 1px;
+ border-style: solid;
+ border-collapse: collapse;
+ width: 100%;
+ clear: both;
+ margin: 0;
+}
+
+.widefat a {
+ text-decoration: none;
+}
+
+.widefat abbr {
+ white-space: nowrap;
+}
+
+.widefat td, .widefat th {
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ border-bottom-color: #ccc;
+ font-size: 11px;
+ vertical-align: text-top;
+}
+
+.widefat td {
+ padding: 7px 15px 9px 10px;
+ vertical-align: top;
+}
+
+.widefat th {
+ padding: 9px 15px 6px 10px;
+ text-align: left;
+ line-height: 1.3em;
+}
+
+.widefat th input {
+ margin: 0 0 0 8px;
+ padding: 0;
+}
+
+.widefat .check-column {
+ text-align: right;
+ width: 1.5em;
+ padding: 0;
+
+}
+.widefat {
+ border-color: #ccc;
+}
+
+.widefat tbody th.check-column {
+ padding: 8px 0 22px;
+}
+.widefat .num {
+ text-align: center;
+}
+.widefat td, .widefat th, div#available-widgets-filter, ul#widget-list li.widget-list-item, .commentlist li {
+ border-bottom-color: #ccc;
+}
+
+.widefat thead, .thead {
+ background-color: #464646;
+ color: #d7d7d7;
+}
+
+td.action-links, th.action-links {
+ text-align: right;
+}
+
+#plugins .name {
+ font-weight: bold;
+}
+
+a {
+color:#2583AD;
+}
+#plugins .active {
+ background-color: #ECF9DD;
+}
+
+
+</style>
+{/literal}
+
+<div style="max-width:980px;">
+
+<h2>Plugins Management</h2>
+<p>Plugins extend and expand the functionality of Piwik. Once a plugin is installed, you may activate it or deactivate it here.</p>
+<table class="widefat">
+ <thead>
+ <tr>
+ <th>Plugin</th>
+ <th class="num">Version</th>
+ <th>Description</th>
+ <th class="status">Status</th>
+ <th class="action-links">Action</th>
+ </tr>
+ </thead>
+ <tbody id="plugins">
+ {foreach from=$pluginsName key=name item=plugin}
+ <tr class={if $plugin.activated}"active"{else}class="deactivate"{/if}>
+ <td class="name">
+ {if isset($plugin.info.homepage)}<a title="Plugin Homepage" href="{$plugin.info.homepage}">{/if}
+ {$name}
+ {if isset($plugin.info.homepage)}</a>{/if}
+ </td>
+ <td class="vers">{$plugin.info.version}</td>
+ <td class="desc">
+ {$plugin.info.description}
+ <cite>By
+ {if isset($plugin.info.author_homepage)}<a title="Author Homepage" href="{$plugin.info.author_homepage}">{/if}
+ {$plugin.info.author}{if isset($plugin.info.author_homepage)}</a>{/if}.</cite>
+ </td>
+ <td class="status">
+ {if $plugin.alwaysActivated}<span title="{'CorePluginsAdmin_ActivatedHelp'|translate}" class="active">Active</span>
+ {elseif $plugin.activated}Active
+ {else}Inactive{/if}
+ </td>
+
+ <td class="togl action-links" {if $plugin.alwaysActivated}title="{'CorePluginsAdmin_ActivatedHelp'|translate}"{/if}>
+ {if $plugin.alwaysActivated} <center>-</center>
+ {elseif $plugin.activated}<a href=?module=CorePluginsAdmin&action=deactivate&pluginName={$name}>{'CorePluginsAdmin_Deactivate'|translate}</a>
+ {else}<a href=?module=CorePluginsAdmin&action=activate&pluginName={$name}>{'CorePluginsAdmin_Activate'|translate}</a>{/if}
+ </td>
+ </tr>
+{/foreach}
+
+</tbody>
+</table>
+
+</div>
\ No newline at end of file |