add icons for Character groups

This commit is contained in:
coderkun 2014-04-29 14:18:04 +02:00
commit 2d9a41a5fe
3461 changed files with 594457 additions and 0 deletions

View file

@ -0,0 +1,481 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
use Piwik\API\Request;
use Piwik\Common;
use Piwik\Filechecks;
use Piwik\Filesystem;
use Piwik\Nonce;
use Piwik\Notification;
use Piwik\Piwik;
use Piwik\Plugin;
use Piwik\Settings\Manager as SettingsManager;
use Piwik\Url;
use Piwik\View;
use Piwik\Version;
use Exception;
/**
*/
class Controller extends Plugin\ControllerAdmin
{
const UPDATE_NONCE = 'CorePluginsAdmin.updatePlugin';
const INSTALL_NONCE = 'CorePluginsAdmin.installPlugin';
const ACTIVATE_NONCE = 'CorePluginsAdmin.activatePlugin';
const DEACTIVATE_NONCE = 'CorePluginsAdmin.deactivatePlugin';
const UNINSTALL_NONCE = 'CorePluginsAdmin.uninstallPlugin';
private $validSortMethods = array('popular', 'newest', 'alpha');
private $defaultSortMethod = 'popular';
private function createUpdateOrInstallView($template, $nonceName)
{
static::dieIfMarketplaceIsDisabled();
$pluginName = $this->initPluginModification($nonceName);
$this->dieIfPluginsAdminIsDisabled();
$view = $this->configureView('@CorePluginsAdmin/' . $template);
$view->plugin = array('name' => $pluginName);
try {
$pluginInstaller = new PluginInstaller($pluginName);
$pluginInstaller->installOrUpdatePluginFromMarketplace();
} catch (\Exception $e) {
$notification = new Notification($e->getMessage());
$notification->context = Notification::CONTEXT_ERROR;
Notification\Manager::notify('CorePluginsAdmin_InstallPlugin', $notification);
$this->redirectAfterModification(true);
return;
}
$marketplace = new Marketplace();
$view->plugin = $marketplace->getPluginInfo($pluginName);
return $view;
}
public function updatePlugin()
{
$view = $this->createUpdateOrInstallView('updatePlugin', static::UPDATE_NONCE);
return $view->render();
}
public function installPlugin()
{
$view = $this->createUpdateOrInstallView('installPlugin', static::INSTALL_NONCE);
$view->nonce = Nonce::getNonce(static::ACTIVATE_NONCE);
return $view->render();
}
public function uploadPlugin()
{
static::dieIfPluginsAdminIsDisabled();
Piwik::checkUserHasSuperUserAccess();
$nonce = Common::getRequestVar('nonce', null, 'string');
if (!Nonce::verifyNonce(static::INSTALL_NONCE, $nonce)) {
throw new \Exception(Piwik::translate('General_ExceptionNonceMismatch'));
}
Nonce::discardNonce(static::INSTALL_NONCE);
if (empty($_FILES['pluginZip'])) {
throw new \Exception('You did not specify a ZIP file.');
}
if (!empty($_FILES['pluginZip']['error'])) {
throw new \Exception('Something went wrong during the plugin file upload. Please try again.');
}
$file = $_FILES['pluginZip']['tmp_name'];
if (!file_exists($file)) {
throw new \Exception('Something went wrong during the plugin file upload. Please try again.');
}
$view = $this->configureView('@CorePluginsAdmin/uploadPlugin');
$pluginInstaller = new PluginInstaller('uploaded');
$pluginMetadata = $pluginInstaller->installOrUpdatePluginFromFile($file);
$view->nonce = Nonce::getNonce(static::ACTIVATE_NONCE);
$view->plugin = array(
'name' => $pluginMetadata->name,
'version' => $pluginMetadata->version,
'isTheme' => !empty($pluginMetadata->theme),
'isActivated' => \Piwik\Plugin\Manager::getInstance()->isPluginActivated($pluginMetadata->name)
);
return $view->render();
}
public function pluginDetails()
{
static::dieIfMarketplaceIsDisabled();
$pluginName = Common::getRequestVar('pluginName', null, 'string');
$activeTab = Common::getRequestVar('activeTab', '', 'string');
if ('changelog' !== $activeTab) {
$activeTab = '';
}
$view = $this->configureView('@CorePluginsAdmin/pluginDetails');
try {
$marketplace = new Marketplace();
$view->plugin = $marketplace->getPluginInfo($pluginName);
$view->isSuperUser = Piwik::hasUserSuperUserAccess();
$view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
$view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
$view->activeTab = $activeTab;
} catch (\Exception $e) {
$view->errorMessage = $e->getMessage();
}
return $view->render();
}
private function dieIfMarketplaceIsDisabled()
{
if (!CorePluginsAdmin::isMarketplaceEnabled()) {
throw new \Exception('The Marketplace feature has been disabled.
You may enable the Marketplace by changing the config entry "enable_marketplace" to 1.
Please contact your Piwik admins with your request so they can assist.');
}
$this->dieIfPluginsAdminIsDisabled();
}
private function dieIfPluginsAdminIsDisabled()
{
if (!CorePluginsAdmin::isPluginsAdminEnabled()) {
throw new \Exception('Enabling, disabling and uninstalling plugins has been disabled by Piwik admins.
Please contact your Piwik admins with your request so they can assist you.');
}
}
private function createBrowsePluginsOrThemesView($template, $themesOnly)
{
static::dieIfMarketplaceIsDisabled();
$query = Common::getRequestVar('query', '', 'string', $_POST);
$sort = Common::getRequestVar('sort', $this->defaultSortMethod, 'string');
if (!in_array($sort, $this->validSortMethods)) {
$sort = $this->defaultSortMethod;
}
$view = $this->configureView('@CorePluginsAdmin/' . $template);
$marketplace = new Marketplace();
$view->plugins = $marketplace->searchPlugins($query, $sort, $themesOnly);
$view->query = $query;
$view->sort = $sort;
$view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
$view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
$view->isSuperUser = Piwik::hasUserSuperUserAccess();
return $view;
}
public function browsePlugins()
{
$view = $this->createBrowsePluginsOrThemesView('browsePlugins', $themesOnly = false);
return $view->render();
}
public function browseThemes()
{
$view = $this->createBrowsePluginsOrThemesView('browseThemes', $themesOnly = true);
return $view->render();
}
public function extend()
{
static::dieIfMarketplaceIsDisabled();
$view = $this->configureView('@CorePluginsAdmin/extend');
$view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
$view->isSuperUser = Piwik::hasUserSuperUserAccess();
return $view->render();
}
private function createPluginsOrThemesView($template, $themesOnly)
{
Piwik::checkUserHasSuperUserAccess();
$view = $this->configureView('@CorePluginsAdmin/' . $template);
$view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
$view->activateNonce = Nonce::getNonce(static::ACTIVATE_NONCE);
$view->uninstallNonce = Nonce::getNonce(static::UNINSTALL_NONCE);
$view->deactivateNonce = Nonce::getNonce(static::DEACTIVATE_NONCE);
$view->pluginsInfo = $this->getPluginsInfo($themesOnly);
$users = \Piwik\Plugins\UsersManager\API::getInstance()->getUsers();
$view->otherUsersCount = count($users) - 1;
$view->themeEnabled = \Piwik\Plugin\Manager::getInstance()->getThemeEnabled()->getPluginName();
$view->pluginNamesHavingSettings = $this->getPluginNamesHavingSettingsForCurrentUser();
$view->isMarketplaceEnabled = CorePluginsAdmin::isMarketplaceEnabled();
$view->isPluginsAdminEnabled = CorePluginsAdmin::isPluginsAdminEnabled();
$view->pluginsHavingUpdate = array();
$view->marketplacePluginNames = array();
if (CorePluginsAdmin::isMarketplaceEnabled()) {
try {
$marketplace = new Marketplace();
$view->marketplacePluginNames = $marketplace->getAvailablePluginNames($themesOnly);
$view->pluginsHavingUpdate = $marketplace->getPluginsHavingUpdate($themesOnly);
} catch(Exception $e) {
// curl exec connection error (ie. server not connected to internet)
}
}
return $view;
}
public function plugins()
{
$view = $this->createPluginsOrThemesView('plugins', $themesOnly = false);
return $view->render();
}
public function themes()
{
$view = $this->createPluginsOrThemesView('themes', $themesOnly = true);
return $view->render();
}
protected function configureView($template)
{
Piwik::checkUserIsNotAnonymous();
$view = new View($template);
$this->setBasicVariablesView($view);
// If user can manage plugins+themes, display a warning if config not writable
if (CorePluginsAdmin::isPluginsAdminEnabled()) {
$this->displayWarningIfConfigFileNotWritable();
}
$view->errorMessage = '';
return $view;
}
protected function getPluginsInfo($themesOnly = false)
{
$pluginManager = \Piwik\Plugin\Manager::getInstance();
$plugins = $pluginManager->returnLoadedPluginsInfo();
foreach ($plugins as $pluginName => &$plugin) {
$plugin['isCorePlugin'] = $pluginManager->isPluginBundledWithCore($pluginName);
if (!isset($plugin['info'])) {
$suffix = Piwik::translate('CorePluginsAdmin_PluginNotWorkingAlternative');
// If the plugin has been renamed, we do not show message to ask user to update plugin
if($pluginName != Request::renameModule($pluginName)) {
$suffix = "You may uninstall the plugin or manually delete the files in piwik/plugins/$pluginName/";
}
$description = '<strong><em>'
. Piwik::translate('CorePluginsAdmin_PluginNotCompatibleWith', array($pluginName, self::getPiwikVersion()))
. '</strong><br/>'
. $suffix
. '</em>';
$plugin['info'] = array(
'description' => $description,
'version' => Piwik::translate('General_Unknown'),
'theme' => false,
);
}
}
$pluginsFiltered = $this->keepPluginsOrThemes($themesOnly, $plugins);
return $pluginsFiltered;
}
protected function keepPluginsOrThemes($themesOnly, $plugins)
{
$pluginsFiltered = array();
foreach ($plugins as $name => $thisPlugin) {
$isTheme = false;
if (!empty($thisPlugin['info']['theme'])) {
$isTheme = (bool)$thisPlugin['info']['theme'];
}
if (($themesOnly && $isTheme)
|| (!$themesOnly && !$isTheme)
) {
$pluginsFiltered[$name] = $thisPlugin;
}
}
return $pluginsFiltered;
}
public function safemode($lastError = array())
{
if (empty($lastError)) {
$lastError = array(
'message' => Common::getRequestVar('error_message', null, 'string'),
'file' => Common::getRequestVar('error_file', null, 'string'),
'line' => Common::getRequestVar('error_line', null, 'integer')
);
}
$outputFormat = Common::getRequestVar('format', 'html', 'string');
$outputFormat = strtolower($outputFormat);
if (!empty($outputFormat) && 'html' !== $outputFormat) {
$errorMessage = $lastError['message'];
if (Piwik::isUserIsAnonymous()) {
$errorMessage = 'A fatal error occurred.';
}
$response = new \Piwik\API\ResponseBuilder($outputFormat);
$message = $response->getResponseException(new Exception($errorMessage));
return $message;
}
if(Common::isPhpCliMode()) {
Piwik_ExitWithMessage("Error:" . var_export($lastError, true));
}
$view = new View('@CorePluginsAdmin/safemode');
$view->lastError = $lastError;
$view->isSuperUser = Piwik::hasUserSuperUserAccess();
$view->isAnonymousUser = Piwik::isUserIsAnonymous();
$view->plugins = Plugin\Manager::getInstance()->returnLoadedPluginsInfo();
$view->deactivateNonce = Nonce::getNonce(static::DEACTIVATE_NONCE);
$view->uninstallNonce = Nonce::getNonce(static::UNINSTALL_NONCE);
$view->emailSuperUser = implode(',', Piwik::getAllSuperUserAccessEmailAddresses());
$view->piwikVersion = Version::VERSION;
$view->showVersion = !Common::getRequestVar('tests_hide_piwik_version', 0);
$view->pluginCausesIssue = '';
if (!empty($lastError['file'])) {
preg_match('/piwik\/plugins\/(.*)\//', $lastError['file'], $matches);
if (!empty($matches[1])) {
$view->pluginCausesIssue = $matches[1];
}
}
return $view->render();
}
public function activate($redirectAfter = true)
{
$pluginName = $this->initPluginModification(static::ACTIVATE_NONCE);
$this->dieIfPluginsAdminIsDisabled();
\Piwik\Plugin\Manager::getInstance()->activatePlugin($pluginName);
if ($redirectAfter) {
$plugin = \Piwik\Plugin\Manager::getInstance()->loadPlugin($pluginName);
$actionToRedirect = 'plugins';
if ($plugin->isTheme()) {
$actionToRedirect = 'themes';
}
$message = Piwik::translate('CorePluginsAdmin_SuccessfullyActicated', array($pluginName));
if (SettingsManager::hasPluginSettingsForCurrentUser($pluginName)) {
$target = sprintf('<a href="index.php%s#%s">',
Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'pluginSettings')),
$pluginName);
$message .= ' ' . Piwik::translate('CorePluginsAdmin_ChangeSettingsPossible', array($target, '</a>'));
}
$notification = new Notification($message);
$notification->raw = true;
$notification->title = Piwik::translate('General_WellDone');
$notification->context = Notification::CONTEXT_SUCCESS;
Notification\Manager::notify('CorePluginsAdmin_PluginActivated', $notification);
$this->redirectToIndex('CorePluginsAdmin', $actionToRedirect);
}
}
public function deactivate($redirectAfter = true)
{
$pluginName = $this->initPluginModification(static::DEACTIVATE_NONCE);
$this->dieIfPluginsAdminIsDisabled();
\Piwik\Plugin\Manager::getInstance()->deactivatePlugin($pluginName);
$this->redirectAfterModification($redirectAfter);
}
public function uninstall($redirectAfter = true)
{
$pluginName = $this->initPluginModification(static::UNINSTALL_NONCE);
$this->dieIfPluginsAdminIsDisabled();
$uninstalled = \Piwik\Plugin\Manager::getInstance()->uninstallPlugin($pluginName);
if (!$uninstalled) {
$path = Filesystem::getPathToPiwikRoot() . '/plugins/' . $pluginName . '/';
$messagePermissions = Filechecks::getErrorMessageMissingPermissions($path);
$messageIntro = Piwik::translate("Warning: \"%s\" could not be uninstalled. Piwik did not have enough permission to delete the files in $path. ",
$pluginName);
$exitMessage = $messageIntro . "<br/><br/>" . $messagePermissions;
$exitMessage .= "<br> Or manually delete this directory (using FTP or SSH access)";
Piwik_ExitWithMessage($exitMessage, $optionalTrace = false, $optionalLinks = false, $optionalLinkBack = true);
}
$this->redirectAfterModification($redirectAfter);
}
protected function initPluginModification($nonceName)
{
Piwik::checkUserHasSuperUserAccess();
$nonce = Common::getRequestVar('nonce', null, 'string');
if (!Nonce::verifyNonce($nonceName, $nonce)) {
throw new \Exception(Piwik::translate('General_ExceptionNonceMismatch'));
}
Nonce::discardNonce($nonceName);
$pluginName = Common::getRequestVar('pluginName', null, 'string');
return $pluginName;
}
protected function redirectAfterModification($redirectAfter)
{
if ($redirectAfter) {
Url::redirectToReferrer();
}
}
private function getPluginNamesHavingSettingsForCurrentUser()
{
return array_keys(SettingsManager::getPluginSettingsForCurrentUser());
}
}

View file

@ -0,0 +1,137 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
use Piwik\Config;
use Piwik\Menu\MenuAdmin;
use Piwik\Piwik;
use Piwik\Plugin;
use Piwik\ScheduledTask;
use Piwik\ScheduledTime;
use Piwik\Plugin\Manager as PluginManager;
/**
*
*/
class CorePluginsAdmin extends \Piwik\Plugin
{
/**
* @see Piwik\Plugin::getListHooksRegistered
*/
public function getListHooksRegistered()
{
return array(
'Menu.Admin.addItems' => 'addMenu',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
);
}
/**
* Gets all scheduled tasks executed by this plugin.
*/
public function getScheduledTasks(&$tasks)
{
$tasks[] = new ScheduledTask(
'Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient',
'clearAllCacheEntries',
null,
ScheduledTime::factory('daily'),
ScheduledTask::LOWEST_PRIORITY
);
if (self::isMarketplaceEnabled()) {
$sendUpdateNotification = new ScheduledTask ($this,
'sendNotificationIfUpdatesAvailable',
null,
ScheduledTime::factory('daily'),
ScheduledTask::LOWEST_PRIORITY);
$tasks[] = $sendUpdateNotification;
}
}
public function sendNotificationIfUpdatesAvailable()
{
$updateCommunication = new UpdateCommunication();
if ($updateCommunication->isEnabled()) {
$updateCommunication->sendNotificationIfUpdatesAvailable();
}
}
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/marketplace.less";
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugins_admin.less";
}
function addMenu()
{
$pluginsUpdateMessage = '';
$themesUpdateMessage = '';
if (Piwik::hasUserSuperUserAccess() && static::isMarketplaceEnabled()) {
$marketplace = new Marketplace();
$pluginsHavingUpdate = $marketplace->getPluginsHavingUpdate($themesOnly = false);
$themesHavingUpdate = $marketplace->getPluginsHavingUpdate($themesOnly = true);
if (!empty($pluginsHavingUpdate)) {
$pluginsUpdateMessage = sprintf(' (%d)', count($pluginsHavingUpdate));
}
if (!empty($themesHavingUpdate)) {
$themesUpdateMessage = sprintf(' (%d)', count($themesHavingUpdate));
}
}
MenuAdmin::getInstance()->add('CorePluginsAdmin_MenuPlatform', null, "", !Piwik::isUserIsAnonymous(), $order = 7);
MenuAdmin::getInstance()->add('CorePluginsAdmin_MenuPlatform', Piwik::translate('General_Plugins') . $pluginsUpdateMessage,
array('module' => 'CorePluginsAdmin', 'action' => 'plugins', 'activated' => ''),
Piwik::hasUserSuperUserAccess(),
$order = 1);
MenuAdmin::getInstance()->add('CorePluginsAdmin_MenuPlatform', Piwik::translate('CorePluginsAdmin_Themes') . $themesUpdateMessage,
array('module' => 'CorePluginsAdmin', 'action' => 'themes', 'activated' => ''),
Piwik::hasUserSuperUserAccess(),
$order = 3);
if (static::isMarketplaceEnabled()) {
MenuAdmin::getInstance()->add('CorePluginsAdmin_MenuPlatform', 'CorePluginsAdmin_Marketplace',
array('module' => 'CorePluginsAdmin', 'action' => 'extend', 'activated' => ''),
!Piwik::isUserIsAnonymous(),
$order = 5);
}
}
public static function isMarketplaceEnabled()
{
return (bool) Config::getInstance()->General['enable_marketplace'];
}
public static function isPluginsAdminEnabled()
{
return (bool) Config::getInstance()->General['enable_plugins_admin'];
}
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/CoreHome/javascripts/popover.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginDetail.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginOverview.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/pluginExtend.js";
$jsFiles[] = "plugins/CorePluginsAdmin/javascripts/plugins.js";
}
public function getClientSideTranslationKeys(&$translations)
{
$translations[] = 'CorePluginsAdmin_NoZipFileSelected';
}
}

View file

@ -0,0 +1,197 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
use Piwik\Date;
use Piwik\Piwik;
use Piwik\Plugin\Dependency as PluginDependency;
/**
*
*/
class Marketplace
{
/**
* @var MarketplaceApiClient
*/
private $client;
public function __construct()
{
$this->client = new MarketplaceApiClient();
}
public function getPluginInfo($pluginName)
{
$marketplace = new MarketplaceApiClient();
$plugin = $marketplace->getPluginInfo($pluginName);
$plugin = $this->enrichPluginInformation($plugin);
return $plugin;
}
public function getAvailablePluginNames($themesOnly)
{
if ($themesOnly) {
$plugins = $this->client->searchForThemes('', '', '');
} else {
$plugins = $this->client->searchForPlugins('', '', '');
}
$names = array();
foreach ($plugins as $plugin) {
$names[] = $plugin['name'];
}
return $names;
}
public function getAllAvailablePluginNames()
{
return array_merge(
$this->getAvailablePluginNames(true),
$this->getAvailablePluginNames(false)
);
}
public function searchPlugins($query, $sort, $themesOnly)
{
if ($themesOnly) {
$plugins = $this->client->searchForThemes('', $query, $sort);
} else {
$plugins = $this->client->searchForPlugins('', $query, $sort);
}
foreach ($plugins as $key => $plugin) {
$plugins[$key] = $this->enrichPluginInformation($plugin);
}
return $plugins;
}
private function getPluginUpdateInformation($plugin)
{
if (empty($plugin['name'])) {
return;
}
$pluginsHavingUpdate = $this->getPluginsHavingUpdate($plugin['isTheme']);
foreach ($pluginsHavingUpdate as $pluginHavingUpdate) {
if ($plugin['name'] == $pluginHavingUpdate['name']) {
return $pluginHavingUpdate;
}
}
}
private function hasPluginUpdate($plugin)
{
$update = $this->getPluginUpdateInformation($plugin);
return !empty($update);
}
/**
* @param bool $themesOnly
* @return array
*/
public function getPluginsHavingUpdate($themesOnly)
{
$pluginManager = \Piwik\Plugin\Manager::getInstance();
$pluginManager->returnLoadedPluginsInfo();
$loadedPlugins = $pluginManager->getLoadedPlugins();
try {
$pluginsHavingUpdate = $this->client->getInfoOfPluginsHavingUpdate($loadedPlugins, $themesOnly);
} catch (\Exception $e) {
$pluginsHavingUpdate = array();
}
foreach ($pluginsHavingUpdate as &$updatePlugin) {
foreach ($loadedPlugins as $loadedPlugin) {
if (!empty($updatePlugin['name'])
&& $loadedPlugin->getPluginName() == $updatePlugin['name']
) {
$updatePlugin['currentVersion'] = $loadedPlugin->getVersion();
$updatePlugin['isActivated'] = $pluginManager->isPluginActivated($updatePlugin['name']);
$updatePlugin = $this->addMissingRequirements($updatePlugin);
break;
}
}
}
return $pluginsHavingUpdate;
}
private function enrichPluginInformation($plugin)
{
$dateFormat = Piwik::translate('CoreHome_ShortDateFormatWithYear');
$plugin['canBeUpdated'] = $this->hasPluginUpdate($plugin);
$plugin['isInstalled'] = \Piwik\Plugin\Manager::getInstance()->isPluginLoaded($plugin['name']);
$plugin['lastUpdated'] = Date::factory($plugin['lastUpdated'])->getLocalized($dateFormat);
if ($plugin['canBeUpdated']) {
$pluginUpdate = $this->getPluginUpdateInformation($plugin);
$plugin['repositoryChangelogUrl'] = $pluginUpdate['repositoryChangelogUrl'];
$plugin['currentVersion'] = $pluginUpdate['currentVersion'];
}
if (!empty($plugin['activity']['lastCommitDate'])
&& false === strpos($plugin['activity']['lastCommitDate'], '0000')) {
$dateFormat = Piwik::translate('CoreHome_DateFormat');
$plugin['activity']['lastCommitDate'] = Date::factory($plugin['activity']['lastCommitDate'])->getLocalized($dateFormat);
} else {
$plugin['activity']['lastCommitDate'] = null;
}
if (!empty($plugin['versions'])) {
$dateFormat = Piwik::translate('CoreHome_DateFormat');
foreach ($plugin['versions'] as $index => $version) {
$plugin['versions'][$index]['release'] = Date::factory($version['release'])->getLocalized($dateFormat);
}
}
$plugin = $this->addMissingRequirements($plugin);
return $plugin;
}
/**
* @param $plugin
*/
private function addMissingRequirements($plugin)
{
$plugin['missingRequirements'] = array();
if (empty($plugin['versions']) || !is_array($plugin['versions'])) {
return $plugin;
}
$latestVersion = $plugin['versions'][count($plugin['versions']) - 1];
if (empty($latestVersion['requires'])) {
return $plugin;
}
$requires = $latestVersion['requires'];
$dependency = new PluginDependency();
$plugin['missingRequirements'] = $dependency->getMissingDependencies($requires);
return $plugin;
}
}

View file

@ -0,0 +1,202 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
use Piwik\CacheFile;
use Piwik\Http;
use Piwik\Version;
/**
*
*/
class MarketplaceApiClient
{
const CACHE_TIMEOUT_IN_SECONDS = 1200;
const HTTP_REQUEST_TIMEOUT = 3;
private $domain = 'http://plugins.piwik.org';
/**
* @var CacheFile
*/
private $cache = null;
public function __construct()
{
$this->cache = new CacheFile('marketplace', self::CACHE_TIMEOUT_IN_SECONDS);
}
public static function clearAllCacheEntries()
{
$cache = new CacheFile('marketplace');
$cache->deleteAll();
}
public function getPluginInfo($name)
{
$action = sprintf('plugins/%s/info', $name);
return $this->fetch($action, array());
}
public function download($pluginOrThemeName, $target)
{
$downloadUrl = $this->getDownloadUrl($pluginOrThemeName);
if (empty($downloadUrl)) {
return false;
}
$success = Http::fetchRemoteFile($downloadUrl, $target, 0, static::HTTP_REQUEST_TIMEOUT);
return $success;
}
/**
* @param \Piwik\Plugin[] $plugins
* @return array|mixed
*/
public function checkUpdates($plugins)
{
$params = array();
foreach ($plugins as $plugin) {
$pluginName = $plugin->getPluginName();
if (!\Piwik\Plugin\Manager::getInstance()->isPluginBundledWithCore($pluginName)) {
$params[] = array('name' => $plugin->getPluginName(), 'version' => $plugin->getVersion());
}
}
if (empty($params)) {
return array();
}
$params = array('plugins' => $params);
$hasUpdates = $this->fetch('plugins/checkUpdates', array('plugins' => json_encode($params)));
if (empty($hasUpdates)) {
return array();
}
return $hasUpdates;
}
/**
* @param \Piwik\Plugin[] $plugins
* @param bool $themesOnly
* @return array
*/
public function getInfoOfPluginsHavingUpdate($plugins, $themesOnly)
{
$hasUpdates = $this->checkUpdates($plugins);
$pluginDetails = array();
foreach ($hasUpdates as $pluginHavingUpdate) {
$plugin = $this->getPluginInfo($pluginHavingUpdate['name']);
$plugin['repositoryChangelogUrl'] = $pluginHavingUpdate['repositoryChangelogUrl'];
if (!empty($plugin['isTheme']) == $themesOnly) {
$pluginDetails[] = $plugin;
}
}
return $pluginDetails;
}
public function searchForPlugins($keywords, $query, $sort)
{
$response = $this->fetch('plugins', array('keywords' => $keywords, 'query' => $query, 'sort' => $sort));
if (!empty($response['plugins'])) {
return $response['plugins'];
}
return array();
}
public function searchForThemes($keywords, $query, $sort)
{
$response = $this->fetch('themes', array('keywords' => $keywords, 'query' => $query, 'sort' => $sort));
if (!empty($response['plugins'])) {
return $response['plugins'];
}
return array();
}
private function fetch($action, $params)
{
ksort($params);
$query = http_build_query($params);
$result = $this->getCachedResult($action, $query);
if (false === $result) {
$endpoint = $this->domain . '/api/1.0/';
$url = sprintf('%s%s?%s', $endpoint, $action, $query);
$response = Http::sendHttpRequest($url, static::HTTP_REQUEST_TIMEOUT);
$result = json_decode($response, true);
if (is_null($result)) {
$message = sprintf('There was an error reading the response from the Marketplace: %s. Please try again later.',
substr($response, 0, 50));
throw new MarketplaceApiException($message);
}
if (!empty($result['error'])) {
throw new MarketplaceApiException($result['error']);
}
$this->cacheResult($action, $query, $result);
}
return $result;
}
private function getCachedResult($action, $query)
{
$cacheKey = $this->getCacheKey($action, $query);
return $this->cache->get($cacheKey);
}
private function cacheResult($action, $query, $result)
{
$cacheKey = $this->getCacheKey($action, $query);
$this->cache->set($cacheKey, $result);
}
private function getCacheKey($action, $query)
{
return sprintf('api.1.0.%s.%s', str_replace('/', '.', $action), md5($query));
}
/**
* @param $pluginOrThemeName
* @throws MarketplaceApiException
* @return string
*/
public function getDownloadUrl($pluginOrThemeName)
{
$plugin = $this->getPluginInfo($pluginOrThemeName);
if (empty($plugin['versions'])) {
throw new MarketplaceApiException('Plugin has no versions.');
}
$latestVersion = array_pop($plugin['versions']);
$downloadUrl = $latestVersion['download'];
return $this->domain . $downloadUrl . '?coreVersion=' . Version::VERSION;
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
/**
*/
class MarketplaceApiException extends \Exception
{
}

View file

@ -0,0 +1,294 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
use Piwik\Filechecks;
use Piwik\Filesystem;
use Piwik\Piwik;
use Piwik\SettingsPiwik;
use Piwik\Unzip;
use Piwik\Plugin\Dependency as PluginDependency;
/**
*
*/
class PluginInstaller
{
const PATH_TO_DOWNLOAD = '/tmp/latest/plugins/';
const PATH_TO_EXTRACT = '/plugins/';
private $pluginName;
public function __construct($pluginName)
{
$this->pluginName = $pluginName;
}
public function installOrUpdatePluginFromMarketplace()
{
$tmpPluginZip = PIWIK_USER_PATH . self::PATH_TO_DOWNLOAD . $this->pluginName . '.zip';
$tmpPluginFolder = PIWIK_USER_PATH . self::PATH_TO_DOWNLOAD . $this->pluginName;
$tmpPluginZip = SettingsPiwik::rewriteTmpPathWithHostname($tmpPluginZip);
$tmpPluginFolder = SettingsPiwik::rewriteTmpPathWithHostname($tmpPluginFolder);
try {
$this->makeSureFoldersAreWritable();
$this->makeSurePluginNameIsValid();
$this->downloadPluginFromMarketplace($tmpPluginZip);
$this->extractPluginFiles($tmpPluginZip, $tmpPluginFolder);
$this->makeSurePluginJsonExists($tmpPluginFolder);
$metadata = $this->getPluginMetadataIfValid($tmpPluginFolder);
$this->makeSureThereAreNoMissingRequirements($metadata);
$this->copyPluginToDestination($tmpPluginFolder);
} catch (\Exception $e) {
$this->removeFileIfExists($tmpPluginZip);
$this->removeFolderIfExists($tmpPluginFolder);
throw $e;
}
$this->removeFileIfExists($tmpPluginZip);
$this->removeFolderIfExists($tmpPluginFolder);
}
public function installOrUpdatePluginFromFile($pathToZip)
{
$tmpPluginFolder = PIWIK_USER_PATH . self::PATH_TO_DOWNLOAD . $this->pluginName;
$tmpPluginFolder = SettingsPiwik::rewriteTmpPathWithHostname($tmpPluginFolder);
try {
$this->makeSureFoldersAreWritable();
$this->extractPluginFiles($pathToZip, $tmpPluginFolder);
$this->makeSurePluginJsonExists($tmpPluginFolder);
$metadata = $this->getPluginMetadataIfValid($tmpPluginFolder);
$this->makeSureThereAreNoMissingRequirements($metadata);
$this->pluginName = $metadata->name;
$this->fixPluginFolderIfNeeded($tmpPluginFolder);
$this->copyPluginToDestination($tmpPluginFolder);
} catch (\Exception $e) {
$this->removeFileIfExists($pathToZip);
$this->removeFolderIfExists($tmpPluginFolder);
throw $e;
}
$this->removeFileIfExists($pathToZip);
$this->removeFolderIfExists($tmpPluginFolder);
return $metadata;
}
private function makeSureFoldersAreWritable()
{
Filechecks::dieIfDirectoriesNotWritable(array(self::PATH_TO_DOWNLOAD, self::PATH_TO_EXTRACT));
}
private function downloadPluginFromMarketplace($pluginZipTargetFile)
{
$this->removeFileIfExists($pluginZipTargetFile);
$marketplace = new MarketplaceApiClient();
try {
$marketplace->download($this->pluginName, $pluginZipTargetFile);
} catch (\Exception $e) {
try {
$downloadUrl = $marketplace->getDownloadUrl($this->pluginName);
$errorMessage = sprintf('Failed to download plugin from %s: %s', $downloadUrl, $e->getMessage());
} catch (\Exception $ex) {
$errorMessage = sprintf('Failed to download plugin: %s', $e->getMessage());
}
throw new PluginInstallerException($errorMessage);
}
}
/**
* @param $pluginZipFile
* @param $pathExtracted
* @throws \Exception
*/
private function extractPluginFiles($pluginZipFile, $pathExtracted)
{
$archive = Unzip::factory('PclZip', $pluginZipFile);
$this->removeFolderIfExists($pathExtracted);
if (0 == ($pluginFiles = $archive->extract($pathExtracted))) {
throw new PluginInstallerException(Piwik::translate('CoreUpdater_ExceptionArchiveIncompatible', $archive->errorInfo()));
}
if (0 == count($pluginFiles)) {
throw new PluginInstallerException(Piwik::translate('Plugin Zip File Is Empty'));
}
}
private function makeSurePluginJsonExists($tmpPluginFolder)
{
$pluginJsonPath = $this->getPathToPluginJson($tmpPluginFolder);
if (!file_exists($pluginJsonPath)) {
throw new PluginInstallerException('Plugin is not valid, it is missing the plugin.json file.');
}
}
private function makeSureThereAreNoMissingRequirements($metadata)
{
$requires = array();
if(!empty($metadata->require)) {
$requires = (array) $metadata->require;
}
$dependency = new PluginDependency();
$missingDependencies = $dependency->getMissingDependencies($requires);
if (!empty($missingDependencies)) {
$message = '';
foreach ($missingDependencies as $dep) {
$params = array(ucfirst($dep['requirement']), $dep['actualVersion'], $dep['requiredVersion']);
$message .= Piwik::translate('CorePluginsAdmin_MissingRequirementsNotice', $params);
}
throw new PluginInstallerException($message);
}
}
private function getPluginMetadataIfValid($tmpPluginFolder)
{
$pluginJsonPath = $this->getPathToPluginJson($tmpPluginFolder);
$metadata = file_get_contents($pluginJsonPath);
$metadata = json_decode($metadata);
if (empty($metadata)) {
throw new PluginInstallerException('Plugin is not valid, plugin.json is empty or does not contain valid JSON.');
}
if (empty($metadata->name)) {
throw new PluginInstallerException('Plugin is not valid, the plugin.json file does not specify the plugin name.');
}
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $metadata->name)) {
throw new PluginInstallerException('The plugin name specified in plugin.json contains illegal characters. ' .
'Plugin name can only contain following characters: [a-zA-Z0-9-_].');
}
if (empty($metadata->version)) {
throw new PluginInstallerException('Plugin is not valid, the plugin.json file does not specify the plugin version.');
}
if (empty($metadata->description)) {
throw new PluginInstallerException('Plugin is not valid, the plugin.json file does not specify a description.');
}
return $metadata;
}
private function getPathToPluginJson($tmpPluginFolder)
{
$firstSubFolder = $this->getNameOfFirstSubfolder($tmpPluginFolder);
$path = $tmpPluginFolder . DIRECTORY_SEPARATOR . $firstSubFolder . DIRECTORY_SEPARATOR . 'plugin.json';
return $path;
}
/**
* @param $pluginDir
* @throws PluginInstallerException
* @return string
*/
private function getNameOfFirstSubfolder($pluginDir)
{
if (!($dir = opendir($pluginDir))) {
return false;
}
$firstSubFolder = '';
while ($file = readdir($dir)) {
if ($file[0] != '.' && is_dir($pluginDir . DIRECTORY_SEPARATOR . $file)) {
$firstSubFolder = $file;
break;
}
}
if (empty($firstSubFolder)) {
throw new PluginInstallerException('The plugin ZIP file does not contain a subfolder, but Piwik expects plugin files to be within a subfolder in the Zip archive.');
}
return $firstSubFolder;
}
private function fixPluginFolderIfNeeded($tmpPluginFolder)
{
$firstSubFolder = $this->getNameOfFirstSubfolder($tmpPluginFolder);
if ($firstSubFolder === $this->pluginName) {
return;
}
$from = $tmpPluginFolder . DIRECTORY_SEPARATOR . $firstSubFolder;
$to = $tmpPluginFolder . DIRECTORY_SEPARATOR . $this->pluginName;
rename($from, $to);
}
private function copyPluginToDestination($tmpPluginFolder)
{
$pluginTargetPath = PIWIK_USER_PATH . self::PATH_TO_EXTRACT . $this->pluginName;
$this->removeFolderIfExists($pluginTargetPath);
Filesystem::copyRecursive($tmpPluginFolder, PIWIK_USER_PATH . self::PATH_TO_EXTRACT);
}
/**
* @param $pathExtracted
*/
private function removeFolderIfExists($pathExtracted)
{
Filesystem::unlinkRecursive($pathExtracted, true);
}
/**
* @param $targetTmpFile
*/
private function removeFileIfExists($targetTmpFile)
{
if (file_exists($targetTmpFile)) {
unlink($targetTmpFile);
}
}
/**
* @throws PluginInstallerException
*/
private function makeSurePluginNameIsValid()
{
try {
$marketplace = new MarketplaceApiClient();
$pluginDetails = $marketplace->getPluginInfo($this->pluginName);
} catch (\Exception $e) {
throw new PluginInstallerException($e->getMessage());
}
if (empty($pluginDetails)) {
throw new PluginInstallerException('This plugin was not found in the Marketplace.');
}
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
/**
* PluginInstallerException
*/
class PluginInstallerException extends \Exception
{
}

View file

@ -0,0 +1,210 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CorePluginsAdmin;
use Piwik\Config;
use Piwik\Mail;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\Plugins\UsersManager\API as UsersManagerApi;
use Piwik\SettingsPiwik;
/**
* Class to check and notify users via email if there are plugin updates available.
*/
class UpdateCommunication
{
private $enabledOptionName = 'enableUpdateCommunicationPlugins';
/**
* Checks whether plugin update notification is enabled or not. If the marketplace is disabled or if update
* communication is disabled in general, it will return false as well.
*
* @return bool
*/
public function isEnabled()
{
if (!$this->canBeEnabled()) {
return false;
}
$isEnabled = Option::get($this->enabledOptionName);
return !empty($isEnabled);
}
/**
* Checks whether a plugin update notification can be enabled or not. It cannot be enabled if for instance the
* Marketplace is disabled or if update notifications are disabled in general.
*
* @return bool
*/
public function canBeEnabled()
{
$isEnabled = Config::getInstance()->General['enable_update_communication'];
return CorePluginsAdmin::isMarketplaceEnabled() && !empty($isEnabled);
}
/**
* Enable plugin update notifications.
*/
public function enable()
{
Option::set($this->enabledOptionName, 1);
}
/**
* Disable plugin update notifications.
*/
public function disable()
{
Option::set($this->enabledOptionName, 0);
}
/**
* Sends an email to all super users if there is an update available for any plugins from the Marketplace.
* For each update we send an email only once.
*
* @return bool
*/
public function sendNotificationIfUpdatesAvailable()
{
$pluginsHavingUpdate = $this->getPluginsHavingUpdate();
if (empty($pluginsHavingUpdate)) {
return;
}
$pluginsToBeNotified = array();
foreach ($pluginsHavingUpdate as $plugin) {
if ($this->hasNotificationAlreadyReceived($plugin)) {
continue;
}
$this->setHasLatestUpdateNotificationReceived($plugin);
$pluginsToBeNotified[] = $plugin;
}
if (!empty($pluginsToBeNotified)) {
$this->sendNotifications($pluginsToBeNotified);
}
}
protected function sendNotifications($pluginsToBeNotified)
{
$hasThemeUpdate = false;
$hasPluginUpdate = false;
foreach ($pluginsToBeNotified as $plugin) {
$hasThemeUpdate = $hasThemeUpdate || $plugin['isTheme'];
$hasPluginUpdate = $hasPluginUpdate || !$plugin['isTheme'];
}
$subject = Piwik::translate('CoreUpdater_NotificationSubjectAvailablePluginUpdate');
$message = Piwik::translate('ScheduledReports_EmailHello');
$message .= "\n\n";
$message .= Piwik::translate('CoreUpdater_ThereIsNewPluginVersionAvailableForUpdate');
$message .= "\n\n";
foreach ($pluginsToBeNotified as $plugin) {
$message .= sprintf(' * %s %s', $plugin['name'], $plugin['latestVersion']);
$message .= "\n";
}
$message .= "\n";
$host = SettingsPiwik::getPiwikUrl();
if ($hasThemeUpdate) {
$message .= Piwik::translate('CoreUpdater_NotificationClickToUpdateThemes') . "\n";
$message .= $host. 'index.php?module=CorePluginsAdmin&action=themes';
}
if ($hasPluginUpdate) {
if ($hasThemeUpdate) {
$message .= "\n\n";
}
$message .= Piwik::translate('CoreUpdater_NotificationClickToUpdatePlugins') . "\n";
$message .= $host. 'index.php?module=CorePluginsAdmin&action=plugins';
}
$message .= "\n\n";
$message .= Piwik::translate('Installation_HappyAnalysing');
$this->sendEmailNotification($subject, $message);
}
/**
* Send an email notification to all super users.
*
* @param $subject
* @param $message
*/
protected function sendEmailNotification($subject, $message)
{
$superUsers = UsersManagerApi::getInstance()->getUsersHavingSuperUserAccess();
foreach ($superUsers as $superUser) {
$mail = new Mail();
$mail->setDefaultFromPiwik();
$mail->addTo($superUser['email']);
$mail->setSubject($subject);
$mail->setBodyText($message);
$mail->send();
}
}
private function setHasLatestUpdateNotificationReceived($plugin)
{
$latestVersion = $this->getLatestVersion($plugin);
Option::set($this->getNotificationSentOptionName($plugin), $latestVersion);
}
private function getLatestVersionSent($plugin)
{
return Option::get($this->getNotificationSentOptionName($plugin));
}
private function getLatestVersion($plugin)
{
return $plugin['latestVersion'];
}
private function hasNotificationAlreadyReceived($plugin)
{
$latestVersion = $this->getLatestVersion($plugin);
$lastVersionSent = $this->getLatestVersionSent($plugin);
if (!empty($lastVersionSent)
&& ($latestVersion == $lastVersionSent
|| version_compare($latestVersion, $lastVersionSent) == -1)) {
return true;
}
return false;
}
private function getNotificationSentOptionName($plugin)
{
return 'last_update_communication_sent_plugin_' . $plugin['name'];
}
protected function getPluginsHavingUpdate()
{
$marketplace = new Marketplace();
$pluginsHavingUpdate = $marketplace->getPluginsHavingUpdate($themesOnly = false);
$themesHavingUpdate = $marketplace->getPluginsHavingUpdate($themesOnly = true);
$plugins = array_merge($pluginsHavingUpdate, $themesHavingUpdate);
return $plugins;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View file

@ -0,0 +1,88 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
$(document).ready(function () {
function syncMaxHeight (selector) {
if (!selector) {
return;
}
var $nodes = $(selector);
if (!$nodes) {
return;
}
var max = {};
$nodes.each(function (index, node) {
var $node = $(node);
var top = $node.position().top;
var height = $node.height();
if (!max[top]) {
max[top] = height;
} else if (max[top] < height) {
max[top] = height;
} else {
$node.height(max[top] + 'px');
}
});
$nodes.each(function (index, node) {
var $node = $(node);
var top = $node.position().top;
$node.height(max[top] + 'px');
});
}
syncMaxHeight('.pluginslist .plugin');
syncMaxHeight('.themeslist .plugin');
$('.pluginslist, #plugins, .themeslist').on('click', '[data-pluginName]', function (event) {
if ($(event.target).hasClass('install') || $(event.target).hasClass('uninstall')) {
return;
}
var pluginName = $(this).attr('data-pluginName');
if (!pluginName) {
return;
}
var activeTab = $(event.target).attr('data-activePluginTab');
if (activeTab) {
pluginName += '!' + activeTab;
}
broadcast.propagateNewPopoverParameter('browsePluginDetail', pluginName);
});
var showPopover = function (value) {
var pluginName = value;
var activeTab = null;
if (-1 !== value.indexOf('!')) {
activeTab = value.substr(value.indexOf('!') + 1);
pluginName = value.substr(0, value.indexOf('!'));
}
var url = 'module=CorePluginsAdmin&action=pluginDetails&pluginName=' + encodeURIComponent(pluginName);
if (activeTab) {
url += '&activeTab=' + encodeURIComponent(activeTab);
}
Piwik_Popover.createPopupAndLoadUrl(url, 'details');
};
broadcast.addPopoverHandler('browsePluginDetail', showPopover);
});

View file

@ -0,0 +1,31 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
$(document).ready(function () {
$('.extendPlatform .uploadPlugin').click(function (event) {
event.preventDefault();
piwikHelper.modalConfirm('#installPluginByUpload', {
yes: function () {
window.location = link;
}
});
});
$('#uploadPluginForm').submit(function (event) {
var $zipFile = $('[name=pluginZip]');
var fileName = $zipFile.val();
if (!fileName || '.zip' != fileName.slice(-4)) {
event.preventDefault();
alert(_pk_translate('CorePluginsAdmin_NoZipFileSelected'));
}
});
});

View file

@ -0,0 +1,37 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
$(document).ready(function () {
var uninstallConfirmMessage = '';
$('#plugins .uninstall').click(function (event) {
event.preventDefault();
var link = $(this).attr('href');
var pluginName = $(this).attr('data-pluginName');
if (!link || !pluginName) {
return;
}
if (!uninstallConfirmMessage) {
uninstallConfirmMessage = $('#uninstallPluginConfirm').text();
}
var messageToDisplay = uninstallConfirmMessage.replace('%s', pluginName);
$('#uninstallPluginConfirm').text(messageToDisplay);
piwikHelper.modalConfirm('#confirmUninstallPlugin', {
yes: function () {
window.location = link;
}
});
});
});

View file

@ -0,0 +1,93 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
$(document).ready(function () {
updateAllNumbersOfMatchingPluginsInFilter();
function filterPlugins()
{
var filterOrigin = getCurrentFilterOrigin();
var filterStatus = getCurrentFilterStatus();
var $nodesToEnable = getMatchingNodes(filterOrigin, filterStatus);
$('#plugins tr[data-filter-origin][data-filter-status]').css('display', 'none');
$nodesToEnable.css('display', 'table-row');
updateAllNumbersOfMatchingPluginsInFilter();
}
function updateAllNumbersOfMatchingPluginsInFilter()
{
var filterOrigin = getCurrentFilterOrigin();
var filterStatus = getCurrentFilterStatus();
updateNumberOfMatchingPluginsInFilter('[data-filter-status="all"]', filterOrigin, 'all');
updateNumberOfMatchingPluginsInFilter('[data-filter-status="active"]', filterOrigin, 'active');
updateNumberOfMatchingPluginsInFilter('[data-filter-status="inactive"]', filterOrigin, 'inactive');
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="all"]', 'all', filterStatus);
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="core"]', 'core', filterStatus);
updateNumberOfMatchingPluginsInFilter('[data-filter-origin="noncore"]', 'noncore', filterStatus);
}
function updateNumberOfMatchingPluginsInFilter(selectorFilterToUpdate, filterOrigin, filterStatus)
{
var numMatchingNodes = getMatchingNodes(filterOrigin, filterStatus).length;
var updatedCounterText = ' (' + numMatchingNodes + ')';
$('.pluginsFilter ' + selectorFilterToUpdate + ' .counter').text(updatedCounterText);
}
function getCurrentFilterOrigin()
{
return $('.pluginsFilter .origin a.active').data('filter-origin');
}
function getCurrentFilterStatus()
{
return $('.pluginsFilter .status a.active').data('filter-status');
}
function getMatchingNodes(filterOrigin, filterStatus)
{
var query = '#plugins tr';
if ('all' == filterOrigin) {
query += '[data-filter-origin]';
} else {
query += '[data-filter-origin=' + filterOrigin + ']';
}
if ('all' == filterStatus) {
query += '[data-filter-status]';
} else {
query += '[data-filter-status=' + filterStatus + ']';
}
return $(query);
}
$('.pluginsFilter .status').on('click', 'a', function (event) {
event.preventDefault();
$(this).siblings().removeClass('active');
$(this).addClass('active');
filterPlugins();
});
$('.pluginsFilter .origin').on('click', 'a', function (event) {
event.preventDefault();
$(this).siblings().removeClass('active');
$(this).addClass('active');
filterPlugins();
});
});

View file

@ -0,0 +1,367 @@
.extendPlatform {
min-width: 580px;
.introduction { max-width:980px; }
.byPlugins { width:50%;float:left; }
.byThemes { width:50%;float:left; }
.teaserImage { width: 128px; height: 128px; margin: 64px; }
.header { font-size: 1.6em; }
.callToAction { font-size: 1.1em;line-height: 2em; }
}
#plugins {
.desc .missingRequirementsNotice {
color: red;
}
.plugin-desc-missingrequirements {
font-weight:bold;
font-style: italic;
a {
text-decoration: underline !important;
color: black;
}
}
.settingsLink {
text-align: right;
width: 100%;
display: inline-block;
font-style: italic;
}
}
.admin .pluginsFilter {
color: #666;
.active {
font-weight: bold;
}
a {
color: #255792;
text-decoration: none;
}
a .counter {
color: #999999;
font-weight: normal;
}
a:hover {
text-decoration: underline;
}
.status {
display: inline-block;
margin-left: 20px;
}
.getNewPlugins {
float: right;
}
}
#installPluginByUpload {
.description {
margin-top: 30px;
margin-bottom: 20px;
}
.startUpload {
margin-top: 20px;
margin-bottom: 20px;
}
}
.pluginslist {
margin-top: 20px;
max-width: 980px;
clear: right;
.plugin {
width: 280px;
float: left;
border: 1px solid #dadada;
padding: 15px;
background-color: #F6F5F3;
margin-right: 14px;
margin-bottom: 15px;
position: relative;
.missingRequirementsNotice,
.updateAvailableNotice {
font-size: 14px;
padding: 10px;
color: #9b7a44;
display: inline-block;
background-color: #ffffe0;
border-radius: 3px;
margin-top: 1px;
margin-bottom: 16px;
a {
color: #9b7a44;
font-weight: bold;
}
}
&:hover {
background-color: #EFEEEC;
}
li {
display: inline-block;
padding-right: 50px;
font-size: 90%;
&.even {
padding-right: 0px;
width: 140px;
overflow: hidden;
white-space: nowrap;
}
&.odd {
padding-right: 0px;
width: 130px;
overflow: hidden;
white-space: nowrap;
}
}
ul {
list-style: none;
margin-left: 0;
line-height: 140%;
}
.header {
margin-top: 0px;
margin-bottom: 15px;
}
.description {
padding-bottom: 10px;
}
.install {
float: right;
}
.update {
.install
}
.more {
font-weight: bold;
text-decoration: none;
color: #255792;
}
.content {
margin-bottom: 46px;
cursor: pointer;
}
.featuredIcon {
margin-right: 3px;
margin-bottom: 3px;
height: 24px;
width: 24px;
position: absolute;
right: 1px;
margin-top: -22px;
}
.footer {
position: absolute;
bottom: 4px;
left: 0px;
right: 0px;
cursor: pointer;
}
.metadataSeparator {
background-color: lightgray;
color: #333;
border: 0px;
height: 1px;
width: 100%;
}
.metadata {
margin-top: 10px;
margin-left: 15px;
margin-right: 15px;
}
}
&.themes .plugin {
.header {
display: inline;
}
.content {
margin-bottom: 57px;
}
.preview {
width: 250px;
height: 250px;
}
.footer {
height: 55px;
position: absolute;
bottom: 7px;
left: 0px;
right: 0px;
}
}
}
.pluginslistNonSuperUserHint {
margin-top: 30px;
margin-bottom: 30px;
width: 500px;
}
.pluginslistActionBar {
min-width: 650px;
max-width: 980px;
form {
display: inline;
}
.sort {
.active {
font-weight: bold;
}
}
.infoBox {
margin: 0px 0px 20px 0px;
}
}
.pluginDetails {
font-size: 13px;
text-align: left;
font-family: Arial, Helvetica, sans-serif;
line-height: 20px;
h3, h4, h5, h6 {
margin: 20px 0px 10px 0px;
color: #000000;
}
.ui-tabs-panel ul, .ui-tabs-panel ol {
list-style: initial;
padding-left: 20px;
}
.content .missingRequirementsNotice,
.content .updateAvailableNotice {
font-size: 14px;
padding: 10px;
color: #9b7a44;
display: inline-block;
background-color: #ffffe0;
border-radius: 3px;
a {
color: #9b7a44;
font-weight: bold;
}
a:hover {
text-decoration: underline;
}
}
p, .ui-tabs-panel ul, .ui-tabs-panel li {
font-family: Arial, Helvetica, sans-serif;
text-align: left;
line-height: 20px;
}
.header .intro {
margin-bottom: 15px;
}
.content p {
margin: 0 0 10px;
}
.description {
padding-right: 25px;
}
.ui-tabs {
padding: 0em;
}
.ui-tabs .ui-tabs-nav {
padding: 0em;
border-bottom: 1px solid #cccccc;
margin-right: 25px;
border-radius: 0px;
font-size: 15px;
}
.ui-tabs .ui-tabs-panel {
padding: 1.4em 3em 0em 0em;
}
.content a {
color: #255792;
text-decoration: none;
}
.metadata dl {
padding-right: 25px;
}
.metadata a:hover {
text-decoration: underline;
}
.ui-state-default {
border: 0px !important;
}
.ui-state-active {
padding-bottom: 0px !important;
}
.ui-state-active.ui-state-default {
border: 1px solid #cccccc !important;
}
.ui-state-default:hover {
background-color: #eeeeee !important;
}
.install {
padding: 11px 19px;
font-size: 17.5px;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
color: #ffffff;
background-color: #5bb75b;
display: inline-block;
text-decoration: none;
}
.install:hover {
text-decoration: underline;
}
dt {
font-weight: bold;
line-height: 20px;
}
dd {
margin-left: 10px;
line-height: 20px;
}
.featuredIcon {
height: 16px;
width: 16px;
margin-right: 5px;
}
}

View file

@ -0,0 +1,54 @@
table.dataTable tr.active-plugin > td {
background-color:#fff !important;
}
table.dataTable tr.active-plugin:hover > td {
background-color:#fff !important;
}
table.dataTable tr.inactive-plugin > td {
background-color:#ddd !important;
}
table.dataTable tr.inactive-plugin:hover > td {
background-color:#ddd !important;
}
.plugin-desc-text {
margin-top:0em;
margin-bottom:1.5em;
}
.plugin-author {
float:left;
}
.plugin-license {
float:right;
font-style:italic;
}
.plugin-homepage {
font-size:.8em;
font-style:italic;
}
table.entityTable tr td .plugin-homepage a {
text-decoration:none;
}
table.entityTable tr td .plugin-homepage a:hover {
text-decoration:underline;
}
table.entityTable tr td a.uninstall {
color:red;
text-decoration:none;
font-weight:bold;
}
.plugin-version {
font-size:80%;
font-style:italic;
color:#777;
}

View file

@ -0,0 +1,50 @@
{% extends 'admin.twig' %}
{% import '@CorePluginsAdmin/macros.twig' as pluginsMacro %}
{% block content %}
<div class="pluginslistActionBar">
<h2 piwik-enriched-headline
feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}"
>{{ 'CorePluginsAdmin_TeaserExtendPiwikByPlugin'|translate }}</h2>
<div class="infoBox">
{{ 'CorePluginsAdmin_BeCarefulUsingPlugins'|translate }}
</div>
{% include "@CorePluginsAdmin/browsePluginsActions.twig" %}
</div>
{% if not isSuperUser %}
<div class="pluginslistNonSuperUserHint">
{{ 'CorePluginsAdmin_NotAllowedToBrowseMarketplacePlugins'|translate }}
</div>
{% endif %}
<div class="pluginslist">
{% if plugins|length %}
{% for plugin in plugins %}
<div class="plugin">
<div class="content" data-pluginName="{{ plugin.name }}">
{% include "@CorePluginsAdmin/pluginOverview.twig" %}
</div>
<div class="footer" data-pluginName="{{ plugin.name }}">
{% if plugin.featured %}
{{ pluginsMacro.featuredIcon('right') }}
{% endif %}
{% include "@CorePluginsAdmin/pluginMetadata.twig" %}
</div>
</div>
{% endfor %}
{% else %}
{{ 'CorePluginsAdmin_NoPluginsFound'|translate }}
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,12 @@
<div class="sort">
<a href="{{ linkTo({'sort': 'popular', 'query': ''}) }}" {% if 'popular' == sort %}class="active"{% endif %}>{{ 'CorePluginsAdmin_SortByPopular'|translate }}</a>
|
<a href="{{ linkTo({'sort': 'newest', 'query': ''}) }}" {% if 'newest' == sort %}class="active"{% endif %}>{{ 'CorePluginsAdmin_SortByNewest'|translate }}</a>
|
<a href="{{ linkTo({'sort': 'alpha', 'query': ''}) }}" {% if 'alpha' == sort %}class="active"{% endif %}>{{ 'CorePluginsAdmin_SortByAlpha'|translate }}</a>
|
<form action="{{ linkTo({'sort': ''}) }}" method="POST">
<input value="{{ query }}" placeholder="{{ 'General_Search'|translate }}" type="text" name="query"/>
<button type="submit">{{ 'General_Search'|translate }}</button>
</form>
</div>

View file

@ -0,0 +1,45 @@
{% extends 'admin.twig' %}
{% block content %}
<div class="pluginslistActionBar">
<h2 piwik-enriched-headline
feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}"
>{{ 'CorePluginsAdmin_TeaserExtendPiwikByTheme'|translate }}</h2>
<div class="infoBox">
{{ 'CorePluginsAdmin_BeCarefulUsingThemes'|translate }}
</div>
{% include "@CorePluginsAdmin/browsePluginsActions.twig" %}
</div>
{% if not isSuperUser %}
<div class="pluginslistNonSuperUserHint">
{{ 'CorePluginsAdmin_NotAllowedToBrowseMarketplaceThemes'|translate }}
</div>
{% endif %}
<div class="pluginslist themes">
{% if plugins|length %}
{% for plugin in plugins %}
<div class="plugin">
<div class="content" data-pluginName="{{ plugin.name }}">
{% include "@CorePluginsAdmin/themeOverview.twig" %}
</div>
<div class="footer" data-pluginName="{{ plugin.name }}">
{% include "@CorePluginsAdmin/pluginMetadata.twig" %}
</div>
</div>
{% endfor %}
{% else %}
{{ 'CorePluginsAdmin_NoThemesFound'|translate }}
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,70 @@
{% extends 'admin.twig' %}
{% import '@CorePluginsAdmin/macros.twig' as plugins %}
{% block content %}
<div class="extendPlatform">
<div class="ui-confirm" id="installPluginByUpload">
<h2>{{ 'CorePluginsAdmin_TeaserExtendPiwikByUpload'|translate }}</h2>
<p class="description"> {{ 'CorePluginsAdmin_AllowedUploadFormats'|translate }} </p>
<form enctype="multipart/form-data"
method="post"
id="uploadPluginForm"
action="{{ linkTo({'action':'uploadPlugin', 'nonce': installNonce}) }}">
<input type="file" name="pluginZip">
<br />
<input class="startUpload" type="submit" value="{{ 'CorePluginsAdmin_UploadZipFile'|translate }}">
</form>
</div>
<div class="introduction">
<h2 piwik-enriched-headline
feature-name="{{ 'CorePluginsAdmin_Marketplace'|translate }}"
>{{ 'CorePluginsAdmin_TeaserExtendPiwik'|translate }}</h2>
<p>{{ 'CorePluginsAdmin_DownloadAndInstallPluginsFromMarketplace'|translate("<a href='?module=Proxy&action=redirect&url=http://plugins.piwik.org/' target='_blank'>", "</a>")|raw }}</p>
{% set marketplaceSellPluginSubject = 'CorePluginsAdmin_MarketplaceSellPluginSubject'|translate %}
<em>{{ 'CorePluginsAdmin_GetEarlyAccessForPaidPlugins'|translate("<a href='mailto:hello@piwik.org?subject=" ~ marketplaceSellPluginSubject ~ "'>", "</a>")|raw }}</em>
</div>
<div>
<div class="byPlugins">
<h3 class="header">{{ 'CorePluginsAdmin_GetNewFunctionality'|translate }}</h3>
<span class="callToAction">{{ 'CorePluginsAdmin_ByInstallingNewPluginFromMarketplace'|translate("<a href=" ~ linkTo({'action':'browsePlugins', 'sort': ''}) ~ ">", "</a>")|raw }}</span>
<p>
<a href="{{ linkTo({'action':'browsePlugins', 'sort': ''}) }}"><img class="teaserImage" title="{{ 'CorePluginsAdmin_InstallNewPlugins'|translate }}" alt="{{ 'CorePluginsAdmin_InstallNewPlugins'|translate }}" src="plugins/CorePluginsAdmin/images/plugins.png"/></a>
</p>
<span class="callToAction">
{{ 'CorePluginsAdmin_ByWritingOwnPlugin'|translate('<a href="http://developer.piwik.org/guides/getting-started-part-1" target="_blank">', '</a>')|raw }}
{% if isSuperUser %}
<br/>{{ 'CorePluginsAdmin_OrByUploadingAPlugin'|translate('<a href="#" class="uploadPlugin">', '</a>')|raw }}
{% endif %}
</span>
</div>
<div class="byThemes">
<h3 class="header">{{ 'CorePluginsAdmin_EnjoyAnotherLookAndFeelOfThemes'|translate }}</h3>
<span class="callToAction">{{ 'CorePluginsAdmin_ByInstallingNewThemeFromMarketplace'|translate("<a href=" ~ linkTo({'action':'browseThemes', 'sort': ''}) ~ ">", "</a>")|raw }}</span>
<p>
<a href="{{ linkTo({'action':'browseThemes', 'sort': ''}) }}"><img class="teaserImage" alt="{{ 'CorePluginsAdmin_InstallNewThemes'|translate }}" title="{{ 'CorePluginsAdmin_InstallNewThemes'|translate }}" src="plugins/CorePluginsAdmin/images/themes.png"/></a>
</p>
<span class="callToAction">
{{ 'CorePluginsAdmin_ByDesigningOwnTheme'|translate('<a href="http://developer.piwik.org/guides/theming" target="_blank">', '</a>')|raw }}
{% if isSuperUser %}
<br />{{ 'CorePluginsAdmin_OrByUploadingATheme'|translate('<a href="#" class="uploadPlugin">', '</a>')|raw }}
{% endif %}
</span>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,41 @@
{% extends 'admin.twig' %}
{% block content %}
<div style="max-width:980px;">
<h2>{{ 'CorePluginsAdmin_InstallingPlugin'|translate(plugin.name) }}</h2>
<div>
{% if plugin.isTheme %}
<p>{{ 'CorePluginsAdmin_StepDownloadingThemeFromMarketplace'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepUnzippingTheme'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepThemeSuccessfullyInstalled'|translate(plugin.name, plugin.latestVersion) }}</p>
<p><strong><a href="{{ linkTo({'action': 'activate', 'pluginName': plugin.name, 'nonce': nonce}) }}">{{ 'CorePluginsAdmin_ActionActivateTheme'|translate }}</a></strong>
|
<a href="{{ linkTo({'action': 'browseThemes'}) }}">{{ 'CorePluginsAdmin_BackToExtendPiwik'|translate }}</a></p>
{% else %}
<p>{{ 'CorePluginsAdmin_StepDownloadingPluginFromMarketplace'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepUnzippingPlugin'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepPluginSuccessfullyInstalled'|translate(plugin.name, plugin.latestVersion) }}</p>
<p><strong><a href="{{ linkTo({'action': 'activate', 'pluginName': plugin.name, 'nonce': nonce}) }}">{{ 'CorePluginsAdmin_ActionActivatePlugin'|translate }}</a></strong>
|
<a href="{{ linkTo({'action': 'browsePlugins'}) }}">{{ 'CorePluginsAdmin_BackToExtendPiwik'|translate }}</a></p>
{% endif %}
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,256 @@
{% macro tablePluginUpdates(pluginsHavingUpdate, nonce, isTheme) %}
<div class='entityContainer'>
<table class="dataTable entityTable">
<thead>
<tr>
<th>{% if isTheme %}{{ 'CorePluginsAdmin_Theme'|translate }}{% else %}{{ 'General_Plugin'|translate }}{% endif %}</th>
<th class="num">{{ 'CorePluginsAdmin_Version'|translate }}</th>
<th>{{ 'General_Description'|translate }}</th>
<th class="status">{{ 'CorePluginsAdmin_Status'|translate }}</th>
<th class="action-links">{{ 'General_Action'|translate }}</th>
</tr>
</thead>
<tbody id="plugins">
{% for name,plugin in pluginsHavingUpdate %}
<tr {% if plugin.isActivated %}class="active-plugin"{% else %}class="inactive-plugin"{% endif %}>
<td class="name">
<a href="javascript:void(0);" data-pluginName="{{ plugin.name|e('html_attr') }}">
{{ plugin.name }}
</a>
</td>
<td class="vers">
{% if plugin.repositoryChangelogUrl %}
<a href="javascript:void(0);" title="{{ 'CorePluginsAdmin_Changelog'|translate }}" data-activePluginTab="changelog" data-pluginName="{{ plugin.name|e('html_attr') }}">{{ plugin.currentVersion }} => {{ plugin.latestVersion }}</a>
{% else %}
{{ plugin.currentVersion }} => {{ plugin.latestVersion }}
{% endif %}
</td>
<td class="desc">
{{ plugin.description }}
{{ _self.missingRequirementsPleaseUpdateNotice(plugin) }}
</td>
<td class="status">
{% if plugin.isActivated %}
{{ 'CorePluginsAdmin_Active'|translate }}
{% else %}
{{ 'CorePluginsAdmin_Inactive'|translate }}
{% endif %}
</td>
<td class="togl action-links">
{% if 0 == plugin.missingRequirements|length %}
<a href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': nonce}) }}">Update</a>
{% else %}
-
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}
{% macro pluginDeveloper(owner) %}
{% if 'piwik' == owner %}<img title="Piwik" alt="Piwik" style="padding-bottom:2px;height:11px;" src="plugins/Zeitgeist/images/logo-marketplace.png"/>{% else %}{{ owner }}{% endif %}
{% endmacro %}
{% macro featuredIcon(align='') %}
<img class="featuredIcon"
title="{{ 'CorePluginsAdmin_FeaturedPlugin'|translate }}"
src="plugins/CorePluginsAdmin/images/rating_important.png"
align="{{ align }}" />
{% endmacro %}
{% macro pluginsFilter(isTheme, isMarketplaceEnabled) %}
<p class="pluginsFilter entityContainer">
<span class="origin">
<strong>{{ 'CorePluginsAdmin_Origin'|translate }}</strong>
<a data-filter-origin="all" href="#" class="active">{{ 'General_All'|translate }}<span class="counter"></span></a> |
<a data-filter-origin="core" href="#">{{ 'CorePluginsAdmin_OriginCore'|translate }}<span class="counter"></span></a> |
<a data-filter-origin="noncore" href="#">{{ 'CorePluginsAdmin_OriginThirdParty'|translate }}<span class="counter"></span></a>
</span>
<span class="status">
<strong>{{ 'CorePluginsAdmin_Status'|translate }}</strong>
<a data-filter-status="all" href="#" class="active">{{ 'General_All'|translate }}<span class="counter"></span></a> |
<a data-filter-status="active" href="#">{{ 'CorePluginsAdmin_Active'|translate }}<span class="counter"></span></a> |
<a data-filter-status="inactive" href="#">{{ 'CorePluginsAdmin_Inactive'|translate }}<span class="counter"></span></a>
</span>
{% if isMarketplaceEnabled %}
<span class="getNewPlugins">
{% if isTheme %}
<a href="{{ linkTo({'action':'browseThemes', 'sort': ''}) }}">{{ 'CorePluginsAdmin_InstallNewThemes'|translate }}</a>
{% else %}
<a href="{{ linkTo({'action':'browsePlugins', 'sort': ''}) }}">{{ 'CorePluginsAdmin_InstallNewPlugins'|translate }}</a>
{% endif %}
</span>
{% endif %}
</p>
{% endmacro %}
{% macro missingRequirementsPleaseUpdateNotice(plugin) %}
{% if plugin.missingRequirements and 0 < plugin.missingRequirements|length %}
{% for req in plugin.missingRequirements -%}
<p class="missingRequirementsNotice">
{% set requirement = req.requirement|capitalize %}
{% if 'Php' == requirement %}
{% set requirement = 'PHP' %}
{% endif %}
{{ 'CorePluginsAdmin_MissingRequirementsNotice'|translate(requirement, req.actualVersion, req.requiredVersion) }}
</p>
{%- endfor %}
{% endif %}
{% endmacro %}
{% macro missingRequirementsInfo(pluginName, metadata, missingRequirements, marketplacePluginNames) %}
{% set causedBy = '' %}
{% for dependency in missingRequirements %}
{% set causedBy = causedBy ~ dependency.requirement|capitalize ~ ' ' ~ dependency.causedBy %}
{% if not loop.last %}
{% set causedBy = causedBy ~ ', ' %}
{% endif %}
{% endfor %}
{{ 'CorePluginsAdmin_PluginRequirement'|translate(pluginName, causedBy) }}
{% if metadata is defined
and metadata.support is defined
and metadata.support.email
and pluginName not in marketplacePluginNames %}
{{ 'CorePluginsAdmin_EmailToEnquireUpdatedVersion'|translate('<a href="mailto:' ~ metadata.support.email|e('html_attr') ~'">' ~ metadata.support.email ~ '</a>', pluginName)|raw }}
{% endif %}
{% endmacro %}
{% macro tablePlugins(pluginsInfo, pluginNamesHavingSettings, activateNonce, deactivateNonce, uninstallNonce, isTheme, marketplacePluginNames, displayAdminLinks) %}
<div id="confirmUninstallPlugin" class="ui-confirm">
<h2 id="uninstallPluginConfirm">{{ 'CorePluginsAdmin_UninstallConfirm'|translate }}</h2>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
</div>
<div class='entityContainer'>
<table class="dataTable entityTable">
<thead>
<tr>
<th>{% if isTheme %}{{ 'CorePluginsAdmin_Theme'|translate }}{% else %}{{ 'General_Plugin'|translate }}{% endif %}</th>
<th>{{ 'General_Description'|translate }}</th>
<th class="status">{{ 'CorePluginsAdmin_Status'|translate }}</th>
{% if (displayAdminLinks) %}
<th class="action-links">{{ 'General_Action'|translate }}</th>
{% endif %}
</tr>
</thead>
<tbody id="plugins">
{% for name,plugin in pluginsInfo %}
{% set isZeitgeist = isTheme and name == 'Zeitgeist' %}
{% if (plugin.alwaysActivated is defined and not plugin.alwaysActivated) or isTheme %}
<tr {% if plugin.activated %}class="active-plugin"{% else %}class="inactive-plugin"{% endif %} data-filter-status="{% if plugin.activated %}active{% else %}inactive{% endif %}" data-filter-origin="{% if plugin.isCorePlugin %}core{% else %}noncore{% endif %}">
<td class="name" style="white-space:nowrap;">
<a name="{{ name|e('html_attr') }}"></a>
{% if not plugin.isCorePlugin and name in marketplacePluginNames -%}
<a href="javascript:void(0);"
data-pluginName="{{ name|e('html_attr') }}"
>{{ name }}</a>
{%- else %}
{{ name }}
{% endif %}
<span class="plugin-version" {% if plugin.isCorePlugin %}title="{{ 'CorePluginsAdmin_CorePluginTooltip'|translate }}"{% endif %}>({% if plugin.isCorePlugin %}{{ 'CorePluginsAdmin_OriginCore'|translate }}{% else %}v{{ plugin.info.version }}{% endif %})</span>
{% if name in pluginNamesHavingSettings %}
<br /><br />
<a href="{{ linkTo({'module':'CoreAdminHome', 'action': 'pluginSettings'}) }}#{{ name|e('html_attr') }}" class="settingsLink">{{ 'General_Settings'|translate }}</a>
{% endif %}
</td>
<td class="desc">
<div class="plugin-desc-missingrequirements">
{% if plugin.missingRequirements is defined and plugin.missingRequirements %}
{{ _self.missingRequirementsInfo(name, plugin.info, plugin.missingRequirements, marketplacePluginNames) }}
<br />
{% endif %}
</div>
<div class="plugin-desc-text">
{{ plugin.info.description|raw|nl2br }}
{% if plugin.info.homepage|default is not empty and plugin.info.homepage not in [
'http://piwik.org', 'http://www.piwik.org', 'http://piwik.org/', 'http://www.piwik.org/'
] %}
<span class="plugin-homepage">
<a href="{{ plugin.info.homepage }}">({{ 'CorePluginsAdmin_PluginHomepage'|translate|replace({' ': '&nbsp;'})|raw }})</a>
</span>
{% endif %}
</div>
{% if plugin.info.license is defined %}
<div class="plugin-license">
{% if plugin.info.license_homepage is defined %}<a title="{{ 'CorePluginsAdmin_LicenseHomepage'|translate }}" target="_blank" href="{{ plugin.info.license_homepage }}">{% endif %}{{ plugin.info.license }}{% if plugin.info.license_homepage is defined %}</a>{% endif %}
</div>
{% endif %}
{% if plugin.info.authors is defined %}
<div class="plugin-author">
<cite>By
{% if plugin.info.authors is defined -%}
{% spaceless %}
{% for author in plugin.info.authors if author.name %}
{% if author.homepage is defined %}
<a title="{{ 'CorePluginsAdmin_AuthorHomepage'|translate }}" href="{{ author.homepage }}" target="_blank">{{ author.name }}</a>
{% else %}
{{ author.name }}
{% endif %}
{% if loop.index < plugin.info.authors|length %}
,
{% endif %}
{% endfor %}
{% endspaceless %}
{%- endif %}.</cite>
</div>
{% endif %}
</td>
<td class="status" {% if isZeitgeist %}style="border-left-width:0px;"{% endif %}>
{% if not isZeitgeist -%}
{% if plugin.activated %}
{{ 'CorePluginsAdmin_Active'|translate }}
{% else %}
{{ 'CorePluginsAdmin_Inactive'|translate }}
{% if plugin.uninstallable and displayAdminLinks %} <br/> - <a data-pluginName="{{ name|escape('html_attr') }}" class="uninstall" href='index.php?module=CorePluginsAdmin&action=uninstall&pluginName={{ name }}&nonce={{ uninstallNonce }}'>{{ 'CorePluginsAdmin_ActionUninstall'|translate }}</a>{% endif %}
{% endif %}
{%- endif %}
</td>
{% if displayAdminLinks %}
<td class="togl action-links" {% if isZeitgeist %}style="border-left-width:0px;"{% endif %}>
{% if not isZeitgeist -%}
{% if plugin.invalid is defined or plugin.alwaysActivated %}
-
{% else %}
{% if plugin.activated %}
<a href='index.php?module=CorePluginsAdmin&action=deactivate&pluginName={{ name }}&nonce={{ deactivateNonce }}'>{{ 'CorePluginsAdmin_Deactivate'|translate }}</a>
{% elseif plugin.missingRequirements %}
-
{% else %}
<a href='index.php?module=CorePluginsAdmin&action=activate&pluginName={{ name }}&nonce={{ activateNonce }}'>{{ 'CorePluginsAdmin_Activate'|translate }}</a>
{% endif %}
{% endif %}
{%- endif %}
</td>
{% endif %}
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
{% endmacro %}

View file

@ -0,0 +1,204 @@
{% import '@CorePluginsAdmin/macros.twig' as pluginsMacro %}
{% block content %}
<div class="pluginDetails">
{% if errorMessage %}
{{ errorMessage }}
{% elseif plugin %}
{% set latestVersion = plugin.versions[plugin.versions|length - 1] %}
<div class="header">
<div class="intro" style="width:75%;float:left;">
<h2>{{ plugin.name }}</h2>
<p class="description">
{% if plugin.featured %}
{{ pluginsMacro.featuredIcon('left') }}
{% endif %}
{{ plugin.description }}
</p>
</div>
<div class="width:25%;float:left;">
{% if isSuperUser %}
{% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
<a class="install update"
href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}"
>{{ 'CoreUpdater_UpdateTitle'|translate }}</a>
{% elseif plugin.isInstalled %}
{% elseif 0 < plugin.missingRequirements|length %}
{% else %}
<a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
class="install">{{ 'CorePluginsAdmin_ActionInstall'|translate }}</a>
{% endif %}
{% endif %}
</div>
</div>
<div class="content">
<div style="width:75%;float:left;">
<div id="pluginDetailsTabs">
<ul>
<li><a href="#tabs-description">{{ 'General_Description'|translate }}</a></li>
{% if latestVersion.readmeHtml.faq %}
<li><a href="#tabs-faq">{{ 'General_Faq'|translate }}</a></li>
{% endif %}
<li><a href="#tabs-changelog">{{ 'CorePluginsAdmin_Changelog'|translate }}</a></li>
{% if plugin.screenshots|length %}
<li><a href="#tabs-screenshots">{{ 'CorePluginsAdmin_Screenshots'|translate }}</a></li>
{% endif %}
{% if latestVersion.readmeHtml.support %}
<li><a href="#tabs-support">{{ 'CorePluginsAdmin_Support'|translate }}</a></li>
{% endif %}
</ul>
<div id="tabs-description">
{{ pluginsMacro.missingRequirementsPleaseUpdateNotice(plugin) }}
{{ latestVersion.readmeHtml.description|raw }}
</div>
{% if latestVersion.readmeHtml.faq %}
<div id="tabs-faq">
{{ latestVersion.readmeHtml.faq|raw }}
</div>
{% endif %}
<div id="tabs-changelog">
{{ pluginsMacro.missingRequirementsPleaseUpdateNotice(plugin) }}
{% if plugin.canBeUpdated %}
<p class="updateAvailableNotice">{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}
{% if plugin.repositoryChangelogUrl %}<a target="_blank" href="{{ plugin.repositoryChangelogUrl }}">{{ 'CorePluginsAdmin_ViewRepositoryChangelog'|translate }}</a>{% endif %}
</p>
{% endif %}
{% if latestVersion.readmeHtml.changelog %}
{{ latestVersion.readmeHtml.changelog|raw }}
{% endif %}
<h3>{{ 'CorePluginsAdmin_History'|translate }}</h3>
<ul>
{% for version in plugin.versions|reverse %}
<li>
{% set versionName %}
<strong>
{% if version.repositoryChangelogUrl %}
<a target="_blank" title="{{ 'CorePluginsAdmin_Changelog'|translate }}" href="{{ version.repositoryChangelogUrl }}">{{ version.name }}</a>
{% else %}
{{ version.name }}
{% endif %}
</strong>
{% endset %}
{{ 'CorePluginsAdmin_PluginVersionInfo'|translate(versionName, version.release)|raw }}
</li>
{% endfor %}
</ul>
</div>
{% if plugin.screenshots|length %}
<div id="tabs-screenshots">
<div class="thumbnails">
{% for screenshot in plugin.screenshots %}
<div class="thumbnail">
<a href="{{ screenshot }}" target="_blank"><img src="{{ screenshot }}?w=400" width="400" alt=""></a>
<p>
{{ screenshot|split('/')|last|replace({'_': ' ', '.png': '', '.jpg': '', '.jpeg': ''}) }}
</p>
</div>
{% endfor %}
</div>
</div>
{% endif %}
{% if latestVersion.readmeHtml.support %}
<div id="tabs-support">
{{ latestVersion.readmeHtml.support|raw }}
</div>
{% endif %}
</div>
</div>
<div class="metadata" style="width:25%;float:left;">
<p><br /></p>
<dl>
<dt>{{ 'CorePluginsAdmin_Version'|translate }}</dt>
<dd>{{ plugin.latestVersion }}</dd>
<dt>{{ 'CorePluginsAdmin_PluginKeywords'|translate }}</dt>
<dd>{{ plugin.keywords|join(', ') }}</dd>
<dt>{{ 'CorePluginsAdmin_LastUpdated'|translate }}</dt>
<dd>{{ plugin.lastUpdated }}</dd>
<dt>{{ 'General_Downloads'|translate }}</dt>
<dd title="{{ 'CorePluginsAdmin_NumDownloadsLatestVersion'|translate(latestVersion.numDownloads|number_format) }}">{{ plugin.numDownloads }}</dd>
<dt>{{ 'CorePluginsAdmin_Developer'|translate }}</dt>
<dd>{{ pluginsMacro.pluginDeveloper(plugin.owner) }}</dd>
<dt>{{ 'CorePluginsAdmin_Authors'|translate }}</dt>
<dd>{% for author in plugin.authors if author.name %}
{% spaceless %}
{% if author.homepage %}
<a target="_blank" href="{{ author.homepage }}">{{ author.name }}</a>
{% elseif author.email %}
<a href="mailto:{{ author.email|escape('url') }}">{{ author.name }}</a>
{% else %}
{{ author.name }}
{% endif %}
{% if loop.index < plugin.authors|length %}
,
{% endif %}
{% endspaceless %}
{% endfor %}
</dd>
<dt>{{ 'CorePluginsAdmin_Websites'|translate }}</dt>
<dd>
{% if plugin.homepage %}
<a target="_blank" href="{{ plugin.homepage }}">{{ 'CorePluginsAdmin_PluginWebsite'|translate }}</a>,
{% endif %}
<a target="_blank" href="{{ plugin.repositoryUrl }}">GitHub</a></dd>
{% if plugin.activity %}
<dt>{{ 'CorePluginsAdmin_Activity'|translate }}</dt>
<dd>
{{ plugin.activity.numCommits }} commits
{% if plugin.activity.numContributors > 1 %}
{{ 'CorePluginsAdmin_ByXDevelopers'|translate(plugin.activity.numContributors) }}
{% endif %}
{% if plugin.activity.lastCommitDate %}
{{ 'CorePluginsAdmin_LastCommitTime'|translate(plugin.activity.lastCommitDate) }}
{% endif %}</dd>
{% endif %}
</dl>
<br />
</div>
</div>
<script type="text/javascript">
$(function() {
var active = 0;
{% if activeTab %}
var $activeTab = $('#tabs-{{ activeTab|e('js') }}');
if ($activeTab) {
active = $activeTab.index() - 1;
}
{% endif %}
$( "#pluginDetailsTabs" ).tabs({active: active >= 0 ? active : 0});
$('.pluginDetails a').each(function (index, a) {
var link = $(a).attr('href');
if (link && 0 === link.indexOf('http')) {
$(a).attr('target', '_blank');
}
});
});
</script>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,9 @@
{% import '@CorePluginsAdmin/macros.twig' as plugins %}
<hr class="metadataSeparator"/>
<ul class="metadata">
<li class="odd">{{ 'CorePluginsAdmin_Version'|translate }}: <strong>{{ plugin.latestVersion }}</strong></li>
<li class="even">{{ 'CorePluginsAdmin_Updated'|translate }}: <strong>{{ plugin.lastUpdated }}</strong></li>
<li class="odd">{{ 'General_Downloads'|translate }}: <strong>{{ plugin.numDownloads }}</strong></li>
<li class="even">{{ 'CorePluginsAdmin_Developer'|translate }}: <strong>{{ plugins.pluginDeveloper(plugin.owner) }}</strong></li>
</ul>

View file

@ -0,0 +1,30 @@
{% import '@CorePluginsAdmin/macros.twig' as plugins %}
{% if isSuperUser %}
{% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
<a class="update"
href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}"
>{{ 'CoreUpdater_UpdateTitle'|translate }}</a>
{% elseif plugin.isInstalled %}
<span class="install">{{ 'General_Installed'|translate }}</span>
{% elseif 0 < plugin.missingRequirements|length %}
{% else %}
<a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
class="install">{{ 'CorePluginsAdmin_ActionInstall'|translate }}</a>
{% endif %}
{% endif %}
<h3 class="header" title="{{ 'General_MoreDetails'|translate }}">
<a href="javascript:void(0);" class="more">{{ plugin.name }}</a>
</h3>
<p class="description">{{ plugin.description }}
<br />
<a href="javascript:void(0);" title="{{ 'General_MoreDetails'|translate }}" class="more">&gt;&gt; {{ 'General_MoreLowerCase'|translate }}</a>
</p>
{% if plugin.canBeUpdated %}
<p class="updateAvailableNotice" data-activePluginTab="changelog">{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}</p>
{% endif %}
{{ plugins.missingRequirementsPleaseUpdateNotice(plugin) }}

View file

@ -0,0 +1,31 @@
{% extends 'admin.twig' %}
{% import '@CorePluginsAdmin/macros.twig' as plugins %}
{% block content %}
<div style="max-width:980px;">
{% if pluginsHavingUpdate|length %}
<h2>{{ pluginsHavingUpdate|length }} Update(s) available</h2>
<p>{{ 'CorePluginsAdmin_InfoPluginUpdateIsRecommended'|translate }}</p>
{{ plugins.tablePluginUpdates(pluginsHavingUpdate, updateNonce, activateNonce, 0) }}
{% endif %}
<h2 piwik-enriched-headline>{{ 'CorePluginsAdmin_PluginsManagement'|translate }}</h2>
<p>{{ 'CorePluginsAdmin_MainDescription'|translate }}
{% if not isPluginsAdminEnabled %}
<br/>{{ 'CorePluginsAdmin_DoMoreContactPiwikAdmins'|translate }}
{% endif %}
</p>
{{ plugins.pluginsFilter(false, isMarketplaceEnabled) }}
{{ plugins.tablePlugins(pluginsInfo, pluginNamesHavingSettings, activateNonce, deactivateNonce, uninstallNonce, false, marketplacePluginNames, isPluginsAdminEnabled) }}
</div>
{% endblock %}

View file

@ -0,0 +1,114 @@
<html>
<head>
<style type="text/css">
html, body {
background-color: white;
}
td {
border: 1px solid #ccc;
border-collapse: collapse;
padding: 5px;
}
table {
border-collapse: collapse;
border: 0px;
}
a {
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<h1>A fatal error occurred</h1>
<div style="width: 640px">
{% if not isAnonymousUser %}
<p>
The following error just broke Piwik{% if showVersion %} (v{{ piwikVersion }}){% endif %}:
<pre>{{ lastError.message }}</pre>
in
<pre>{{ lastError.file }} line {{ lastError.line }}</pre>
</p>
{% endif %}
{% if isSuperUser %}
<p>
If this error continues to happen, there is a good chance to fix this issue by disabling one or more of
the Third-Party plugins. You can enable them again in the
<a target="_blank" href="index.php?module=CorePluginsAdmin&action=plugins">Plugins</a> or <a target="_blank" href="index.php?module=CorePluginsAdmin&action=themes">Themes</a> page under
settings at any time.
{% if pluginCausesIssue %}
Based on the error message, the issue is probably caused by the plugin <strong>{{ pluginCausesIssue }}</strong>.
{% endif %}
</p>
<table>
{% for pluginName, plugin in plugins if plugin.uninstallable and plugin.activated %}
<tr {% if loop.index is divisibleby(2) %}style="background-color: #eeeeee"{% endif %}>
<td style="min-width:200px;">
{{ pluginName }}
</td>
<td>
<a href="index.php?module=CorePluginsAdmin&action=deactivate&pluginName={{ pluginName }}&nonce={{ deactivateNonce }}"
target="_blank">deactivate</a>
</td>
</tr>
{% endfor %}
</table>
{% set uninstalledPluginsFound = false %}
{% for pluginName, plugin in plugins if plugin.uninstallable and not plugin.activated %}
{% set uninstalledPluginsFound = true %}
{% endfor %}
{% if uninstalledPluginsFound %}
<p>
If this error still occurs after disabling all plugins, you might want to consider uninstalling some
plugins. Keep in mind: The plugin will be completely removed from your platform.
</p>
<table>
{% for pluginName, plugin in plugins if plugin.uninstallable and not plugin.activated %}
<tr {% if loop.index is divisibleby(2) %}style="background-color: #eeeeee"{% endif %}>
<td style="min-width:200px;">
{{ pluginName }}
</td>
<td>
<a href="index.php?module=CorePluginsAdmin&action=uninstall&pluginName={{ pluginName }}&nonce={{ uninstallNonce }}"
target="_blank" onclick="return confirm('{{ 'CorePluginsAdmin_UninstallConfirm'|translate(pluginName)|e('js') }}')">uninstall</a>
</td>
</tr>
{% endfor %}
</table>
{% endif %}
<p>
<br />
We appreciate if you send the
<a href="mailto:hello@piwik.org?subject={{ 'Fatal error in Piwik ' ~ piwikVersion|e('url') }}&body={{ lastError.message|e('url') }}%20in%20{{ lastError.file|e('url') }}%20{{ lastError.line|e('url') }}%20using%20PHP%20{{ constant('PHP_VERSION') }}">error report</a>
to the Piwik team.
</p>
{% elseif isAnonymousUser %}
<p>Please contact the system administrator.</p>
{% else %}
<p>
If this error continues to happen you may want to send an
<a href="mailto:{{ emailSuperUser }}?subject={{ 'Fatal error in Piwik ' ~ piwikVersion|e('url') }}&body={{ lastError.message|e('url') }}%20in%20{{ lastError.file|e('url') }}%20{{ lastError.line|e('url') }}%20using%20PHP%20{{ constant('PHP_VERSION') }}">error report</a>
to your system administrator.
</p>
{% endif %}
</div>
</body>
</html>

View file

@ -0,0 +1,30 @@
{% import '@CorePluginsAdmin/macros.twig' as plugins %}
{% if isSuperUser %}
{% if plugin.canBeUpdated and 0 == plugin.missingRequirements|length %}
<a href="{{ linkTo({'action':'updatePlugin', 'pluginName': plugin.name, 'nonce': updateNonce}) }}"
class="update"
>{{ 'CoreUpdater_UpdateTitle'|translate }}</a>
{% elseif plugin.isInstalled %}
<span class="install">{{ 'General_Installed'|translate }}</span>
{% elseif 0 < plugin.missingRequirements|length %}
{% else %}
<a href="{{ linkTo({'action': 'installPlugin', 'pluginName': plugin.name, 'nonce': installNonce}) }}"
class="install">{{ 'CorePluginsAdmin_ActionInstall'|translate }}</a>
{% endif %}
{% endif %}
<h3 class="header" title="{{ 'General_MoreDetails'|translate }}">
<a href="javascript:void(0);" class="more">{{ plugin.name }}</a>
</h3>
<p class="description">{% if plugin.featured %}{{ plugins.featuredIcon('right') }}{% endif %}{{ plugin.description }}</p>
{% if plugin.canBeUpdated %}
<p class="updateAvailableNotice">{{ 'CorePluginsAdmin_PluginUpdateAvailable'|translate(plugin.currentVersion, plugin.latestVersion) }}</p>
{% endif %}
{{ plugins.missingRequirementsPleaseUpdateNotice(plugin) }}
<a href="javascript:void(0);" class="more"><img title="{{ 'General_MoreDetails'|translate }}"
class="preview" src="{{ plugin.screenshots|first }}?w=250&h=250"/></a>

View file

@ -0,0 +1,33 @@
{% extends 'admin.twig' %}
{% import '@CorePluginsAdmin/macros.twig' as plugins %}
{% block content %}
<div style="max-width:980px;">
{% if pluginsHavingUpdate|length %}
<h2>{{ 'CorePluginsAdmin_NumUpdatesAvailable'|translate(pluginsHavingUpdate|length) }}</h2>
<p>{{ 'CorePluginsAdmin_InfoThemeUpdateIsRecommended'|translate }}</p>
{{ plugins.tablePluginUpdates(pluginsHavingUpdate, updateNonce, true) }}
{% endif %}
<h2 piwik-enriched-headline>{{ 'CorePluginsAdmin_ThemesManagement'|translate }}</h2>
<p>{{ 'CorePluginsAdmin_ThemesDescription'|translate }}
{% if otherUsersCount > 0 %}
<br/> {{ 'CorePluginsAdmin_InfoThemeIsUsedByOtherUsersAsWell'|translate(otherUsersCount, themeEnabled) }}
{% endif %}
{% if not isPluginsAdminEnabled %}
<br/>{{ 'CorePluginsAdmin_DoMoreContactPiwikAdmins'|translate }}
{% endif %}
</p>
{{ plugins.pluginsFilter(true, isMarketplaceEnabled) }}
{{ plugins.tablePlugins(pluginsInfo, pluginNamesHavingSettings, activateNonce, deactivateNonce, uninstallNonce, true, marketplacePluginNames, isPluginsAdminEnabled ) }}
</div>
{% endblock %}

View file

@ -0,0 +1,41 @@
{% extends 'admin.twig' %}
{% block content %}
<div style="max-width:980px;">
<h2>{{ 'CorePluginsAdmin_UpdatingPlugin'|translate(plugin.name) }}</h2>
<div>
{% if plugin.isTheme %}
<p>{{ 'CorePluginsAdmin_StepDownloadingThemeFromMarketplace'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepUnzippingTheme'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepReplaceExistingTheme'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepThemeSuccessfullyUpdated'|translate(plugin.name, plugin.latestVersion) }}</p>
{% else %}
<p>{{ 'CorePluginsAdmin_StepDownloadingPluginFromMarketplace'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepUnzippingPlugin'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepReplaceExistingPlugin'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepPluginSuccessfullyUpdated'|translate(plugin.name, plugin.latestVersion) }}</p>
{% endif %}
<p><a href="{{ linkTo({'action': 'plugins'}) }}">{{ 'General_Plugins'|translate }}</a>
|
<a href="{{ linkTo({'action': 'themes'}) }}">{{ 'CorePluginsAdmin_Themes'|translate }}</a>
|
<a href="{{ linkTo({'action': 'extend'}) }}">{{ 'CorePluginsAdmin_Marketplace'|translate }}</a></p>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,44 @@
{% extends 'admin.twig' %}
{% block content %}
<div style="max-width:980px;">
<div>
<h2>{{ 'CorePluginsAdmin_InstallingPlugin'|translate(plugin.name) }}</h2>
{% if plugin.isTheme %}
<p>{{ 'CorePluginsAdmin_StepUnzippingTheme'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepThemeSuccessfullyInstalled'|translate(plugin.name, plugin.version) }}</p>
<p>
{% if not plugin.isActivated %}
<strong><a href="{{ linkTo({'action': 'activate', 'pluginName': plugin.name, 'nonce': nonce}) }}">{{ 'CorePluginsAdmin_ActionActivateTheme'|translate }}</a></strong>
|
{% endif %}
<a href="{{ linkTo({'action': 'extend'}) }}">{{ 'CorePluginsAdmin_BackToExtendPiwik'|translate }}</a>
</p>
{% else %}
<p>{{ 'CorePluginsAdmin_StepUnzippingPlugin'|translate }}</p>
<p>{{ 'CorePluginsAdmin_StepPluginSuccessfullyInstalled'|translate(plugin.name, plugin.version) }}</p>
<p>
{% if not plugin.isActivated %}
<strong><a href="{{ linkTo({'action': 'activate', 'pluginName': plugin.name, 'nonce': nonce}) }}">{{ 'CorePluginsAdmin_ActionActivatePlugin'|translate }}</a></strong>
|
{% endif %}
<a href="{{ linkTo({'action': 'extend'}) }}">{{ 'CorePluginsAdmin_BackToExtendPiwik'|translate }}</a>
</p>
{% endif %}
</div>
</div>
{% endblock %}