PageRenderTime 54ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/plugins/CorePluginsAdmin/Controller.php

https://github.com/CodeYellowBV/piwik
PHP | 481 lines | 363 code | 106 blank | 12 comment | 31 complexity | 9446199cbd5f2a5fc7b24fd44cec570f MD5 | raw file
Possible License(s): LGPL-3.0, JSON, MIT, GPL-3.0, LGPL-2.1, GPL-2.0, AGPL-1.0, BSD-2-Clause, BSD-3-Clause
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\Plugins\CorePluginsAdmin;
  10. use Exception;
  11. use Piwik\API\Request;
  12. use Piwik\Common;
  13. use Piwik\Filechecks;
  14. use Piwik\Filesystem;
  15. use Piwik\Nonce;
  16. use Piwik\Notification;
  17. use Piwik\Piwik;
  18. use Piwik\Plugin;
  19. use Piwik\Settings\Manager as SettingsManager;
  20. use Piwik\Url;
  21. use Piwik\Version;
  22. use Piwik\View;
  23. /**
  24. */
  25. class Controller extends Plugin\ControllerAdmin
  26. {
  27. const UPDATE_NONCE = 'CorePluginsAdmin.updatePlugin';
  28. const INSTALL_NONCE = 'CorePluginsAdmin.installPlugin';
  29. const ACTIVATE_NONCE = 'CorePluginsAdmin.activatePlugin';
  30. const DEACTIVATE_NONCE = 'CorePluginsAdmin.deactivatePlugin';
  31. const UNINSTALL_NONCE = 'CorePluginsAdmin.uninstallPlugin';
  32. private $validSortMethods = array('popular', 'newest', 'alpha');
  33. private $defaultSortMethod = 'popular';
  34. private function createUpdateOrInstallView($template, $nonceName)
  35. {
  36. static::dieIfMarketplaceIsDisabled();
  37. $pluginName = $this->initPluginModification($nonceName);
  38. $this->dieIfPluginsAdminIsDisabled();
  39. $view = $this->configureView('@CorePluginsAdmin/' . $template);
  40. $view->plugin = array('name' => $pluginName);
  41. try {
  42. $pluginInstaller = new PluginInstaller($pluginName);
  43. $pluginInstaller->installOrUpdatePluginFromMarketplace();
  44. } catch (\Exception $e) {
  45. $notification = new Notification($e->getMessage());
  46. $notification->context = Notification::CONTEXT_ERROR;
  47. Notification\Manager::notify('CorePluginsAdmin_InstallPlugin', $notification);
  48. $this->redirectAfterModification(true);
  49. return;
  50. }
  51. $marketplace = new Marketplace();
  52. $view->plugin = $marketplace->getPluginInfo($pluginName);
  53. return $view;
  54. }
  55. public function updatePlugin()
  56. {
  57. $view = $this->createUpdateOrInstallView('updatePlugin', static::UPDATE_NONCE);
  58. return $view->render();
  59. }
  60. public function installPlugin()
  61. {
  62. $view = $this->createUpdateOrInstallView('installPlugin', static::INSTALL_NONCE);
  63. $view->nonce = Nonce::getNonce(static::ACTIVATE_NONCE);
  64. return $view->render();
  65. }
  66. public function uploadPlugin()
  67. {
  68. static::dieIfPluginsAdminIsDisabled();
  69. Piwik::checkUserHasSuperUserAccess();
  70. $nonce = Common::getRequestVar('nonce', null, 'string');
  71. if (!Nonce::verifyNonce(static::INSTALL_NONCE, $nonce)) {
  72. throw new \Exception(Piwik::translate('General_ExceptionNonceMismatch'));
  73. }
  74. Nonce::discardNonce(static::INSTALL_NONCE);
  75. if (empty($_FILES['pluginZip'])) {
  76. throw new \Exception('You did not specify a ZIP file.');
  77. }
  78. if (!empty($_FILES['pluginZip']['error'])) {
  79. throw new \Exception('Something went wrong during the plugin file upload. Please try again.');
  80. }
  81. $file = $_FILES['pluginZip']['tmp_name'];
  82. if (!file_exists($file)) {
  83. throw new \Exception('Something went wrong during the plugin file upload. Please try again.');
  84. }
  85. $view = $this->configureView('@CorePluginsAdmin/uploadPlugin');
  86. $pluginInstaller = new PluginInstaller('uploaded');
  87. $pluginMetadata = $pluginInstaller->installOrUpdatePluginFromFile($file);
  88. $view->nonce = Nonce::getNonce(static::ACTIVATE_NONCE);
  89. $view->plugin = array(
  90. 'name' => $pluginMetadata->name,
  91. 'version' => $pluginMetadata->version,
  92. 'isTheme' => !empty($pluginMetadata->theme),
  93. 'isActivated' => \Piwik\Plugin\Manager::getInstance()->isPluginActivated($pluginMetadata->name)
  94. );
  95. return $view->render();
  96. }
  97. public function pluginDetails()
  98. {
  99. static::dieIfMarketplaceIsDisabled();
  100. $pluginName = Common::getRequestVar('pluginName', null, 'string');
  101. $activeTab = Common::getRequestVar('activeTab', '', 'string');
  102. if ('changelog' !== $activeTab) {
  103. $activeTab = '';
  104. }
  105. $view = $this->configureView('@CorePluginsAdmin/pluginDetails');
  106. try {
  107. $marketplace = new Marketplace();
  108. $view->plugin = $marketplace->getPluginInfo($pluginName);
  109. $view->isSuperUser = Piwik::hasUserSuperUserAccess();
  110. $view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
  111. $view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
  112. $view->activeTab = $activeTab;
  113. } catch (\Exception $e) {
  114. $view->errorMessage = $e->getMessage();
  115. }
  116. return $view->render();
  117. }
  118. private function dieIfMarketplaceIsDisabled()
  119. {
  120. if (!CorePluginsAdmin::isMarketplaceEnabled()) {
  121. throw new \Exception('The Marketplace feature has been disabled.
  122. You may enable the Marketplace by changing the config entry "enable_marketplace" to 1.
  123. Please contact your Piwik admins with your request so they can assist.');
  124. }
  125. $this->dieIfPluginsAdminIsDisabled();
  126. }
  127. private function dieIfPluginsAdminIsDisabled()
  128. {
  129. if (!CorePluginsAdmin::isPluginsAdminEnabled()) {
  130. throw new \Exception('Enabling, disabling and uninstalling plugins has been disabled by Piwik admins.
  131. Please contact your Piwik admins with your request so they can assist you.');
  132. }
  133. }
  134. private function createBrowsePluginsOrThemesView($template, $themesOnly)
  135. {
  136. static::dieIfMarketplaceIsDisabled();
  137. $query = Common::getRequestVar('query', '', 'string', $_POST);
  138. $sort = Common::getRequestVar('sort', $this->defaultSortMethod, 'string');
  139. if (!in_array($sort, $this->validSortMethods)) {
  140. $sort = $this->defaultSortMethod;
  141. }
  142. $view = $this->configureView('@CorePluginsAdmin/' . $template);
  143. $marketplace = new Marketplace();
  144. $view->plugins = $marketplace->searchPlugins($query, $sort, $themesOnly);
  145. $view->query = $query;
  146. $view->sort = $sort;
  147. $view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
  148. $view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
  149. $view->isSuperUser = Piwik::hasUserSuperUserAccess();
  150. return $view;
  151. }
  152. public function browsePlugins()
  153. {
  154. $view = $this->createBrowsePluginsOrThemesView('browsePlugins', $themesOnly = false);
  155. return $view->render();
  156. }
  157. public function browseThemes()
  158. {
  159. $view = $this->createBrowsePluginsOrThemesView('browseThemes', $themesOnly = true);
  160. return $view->render();
  161. }
  162. public function extend()
  163. {
  164. static::dieIfMarketplaceIsDisabled();
  165. $view = $this->configureView('@CorePluginsAdmin/extend');
  166. $view->installNonce = Nonce::getNonce(static::INSTALL_NONCE);
  167. $view->isSuperUser = Piwik::hasUserSuperUserAccess();
  168. return $view->render();
  169. }
  170. private function createPluginsOrThemesView($template, $themesOnly)
  171. {
  172. Piwik::checkUserHasSuperUserAccess();
  173. $view = $this->configureView('@CorePluginsAdmin/' . $template);
  174. $view->updateNonce = Nonce::getNonce(static::UPDATE_NONCE);
  175. $view->activateNonce = Nonce::getNonce(static::ACTIVATE_NONCE);
  176. $view->uninstallNonce = Nonce::getNonce(static::UNINSTALL_NONCE);
  177. $view->deactivateNonce = Nonce::getNonce(static::DEACTIVATE_NONCE);
  178. $view->pluginsInfo = $this->getPluginsInfo($themesOnly);
  179. $users = \Piwik\Plugins\UsersManager\API::getInstance()->getUsers();
  180. $view->otherUsersCount = count($users) - 1;
  181. $view->themeEnabled = \Piwik\Plugin\Manager::getInstance()->getThemeEnabled()->getPluginName();
  182. $view->pluginNamesHavingSettings = $this->getPluginNamesHavingSettingsForCurrentUser();
  183. $view->isMarketplaceEnabled = CorePluginsAdmin::isMarketplaceEnabled();
  184. $view->isPluginsAdminEnabled = CorePluginsAdmin::isPluginsAdminEnabled();
  185. $view->pluginsHavingUpdate = array();
  186. $view->marketplacePluginNames = array();
  187. if (CorePluginsAdmin::isMarketplaceEnabled()) {
  188. try {
  189. $marketplace = new Marketplace();
  190. $view->marketplacePluginNames = $marketplace->getAvailablePluginNames($themesOnly);
  191. $view->pluginsHavingUpdate = $marketplace->getPluginsHavingUpdate($themesOnly);
  192. } catch(Exception $e) {
  193. // curl exec connection error (ie. server not connected to internet)
  194. }
  195. }
  196. return $view;
  197. }
  198. public function plugins()
  199. {
  200. $view = $this->createPluginsOrThemesView('plugins', $themesOnly = false);
  201. return $view->render();
  202. }
  203. public function themes()
  204. {
  205. $view = $this->createPluginsOrThemesView('themes', $themesOnly = true);
  206. return $view->render();
  207. }
  208. protected function configureView($template)
  209. {
  210. Piwik::checkUserIsNotAnonymous();
  211. $view = new View($template);
  212. $this->setBasicVariablesView($view);
  213. // If user can manage plugins+themes, display a warning if config not writable
  214. if (CorePluginsAdmin::isPluginsAdminEnabled()) {
  215. $this->displayWarningIfConfigFileNotWritable();
  216. }
  217. $view->errorMessage = '';
  218. return $view;
  219. }
  220. protected function getPluginsInfo($themesOnly = false)
  221. {
  222. $pluginManager = \Piwik\Plugin\Manager::getInstance();
  223. $plugins = $pluginManager->returnLoadedPluginsInfo();
  224. foreach ($plugins as $pluginName => &$plugin) {
  225. $plugin['isCorePlugin'] = $pluginManager->isPluginBundledWithCore($pluginName);
  226. if (!isset($plugin['info'])) {
  227. $suffix = Piwik::translate('CorePluginsAdmin_PluginNotWorkingAlternative');
  228. // If the plugin has been renamed, we do not show message to ask user to update plugin
  229. if($pluginName != Request::renameModule($pluginName)) {
  230. $suffix = "You may uninstall the plugin or manually delete the files in piwik/plugins/$pluginName/";
  231. }
  232. $description = '<strong><em>'
  233. . Piwik::translate('CorePluginsAdmin_PluginNotCompatibleWith', array($pluginName, self::getPiwikVersion()))
  234. . '</strong><br/>'
  235. . $suffix
  236. . '</em>';
  237. $plugin['info'] = array(
  238. 'description' => $description,
  239. 'version' => Piwik::translate('General_Unknown'),
  240. 'theme' => false,
  241. );
  242. }
  243. }
  244. $pluginsFiltered = $this->keepPluginsOrThemes($themesOnly, $plugins);
  245. return $pluginsFiltered;
  246. }
  247. protected function keepPluginsOrThemes($themesOnly, $plugins)
  248. {
  249. $pluginsFiltered = array();
  250. foreach ($plugins as $name => $thisPlugin) {
  251. $isTheme = false;
  252. if (!empty($thisPlugin['info']['theme'])) {
  253. $isTheme = (bool)$thisPlugin['info']['theme'];
  254. }
  255. if (($themesOnly && $isTheme)
  256. || (!$themesOnly && !$isTheme)
  257. ) {
  258. $pluginsFiltered[$name] = $thisPlugin;
  259. }
  260. }
  261. return $pluginsFiltered;
  262. }
  263. public function safemode($lastError = array())
  264. {
  265. if (empty($lastError)) {
  266. $lastError = array(
  267. 'message' => Common::getRequestVar('error_message', null, 'string'),
  268. 'file' => Common::getRequestVar('error_file', null, 'string'),
  269. 'line' => Common::getRequestVar('error_line', null, 'integer')
  270. );
  271. }
  272. $outputFormat = Common::getRequestVar('format', 'html', 'string');
  273. $outputFormat = strtolower($outputFormat);
  274. if (!empty($outputFormat) && 'html' !== $outputFormat) {
  275. $errorMessage = $lastError['message'];
  276. if (Piwik::isUserIsAnonymous()) {
  277. $errorMessage = 'A fatal error occurred.';
  278. }
  279. $response = new \Piwik\API\ResponseBuilder($outputFormat);
  280. $message = $response->getResponseException(new Exception($errorMessage));
  281. return $message;
  282. }
  283. if(Common::isPhpCliMode()) {
  284. Piwik_ExitWithMessage("Error:" . var_export($lastError, true));
  285. }
  286. $view = new View('@CorePluginsAdmin/safemode');
  287. $view->lastError = $lastError;
  288. $view->isSuperUser = Piwik::hasUserSuperUserAccess();
  289. $view->isAnonymousUser = Piwik::isUserIsAnonymous();
  290. $view->plugins = Plugin\Manager::getInstance()->returnLoadedPluginsInfo();
  291. $view->deactivateNonce = Nonce::getNonce(static::DEACTIVATE_NONCE);
  292. $view->uninstallNonce = Nonce::getNonce(static::UNINSTALL_NONCE);
  293. $view->emailSuperUser = implode(',', Piwik::getAllSuperUserAccessEmailAddresses());
  294. $view->piwikVersion = Version::VERSION;
  295. $view->showVersion = !Common::getRequestVar('tests_hide_piwik_version', 0);
  296. $view->pluginCausesIssue = '';
  297. if (!empty($lastError['file'])) {
  298. preg_match('/piwik\/plugins\/(.*)\//', $lastError['file'], $matches);
  299. if (!empty($matches[1])) {
  300. $view->pluginCausesIssue = $matches[1];
  301. }
  302. }
  303. return $view->render();
  304. }
  305. public function activate($redirectAfter = true)
  306. {
  307. $pluginName = $this->initPluginModification(static::ACTIVATE_NONCE);
  308. $this->dieIfPluginsAdminIsDisabled();
  309. \Piwik\Plugin\Manager::getInstance()->activatePlugin($pluginName);
  310. if ($redirectAfter) {
  311. $plugin = \Piwik\Plugin\Manager::getInstance()->loadPlugin($pluginName);
  312. $actionToRedirect = 'plugins';
  313. if ($plugin->isTheme()) {
  314. $actionToRedirect = 'themes';
  315. }
  316. $message = Piwik::translate('CorePluginsAdmin_SuccessfullyActicated', array($pluginName));
  317. if (SettingsManager::hasPluginSettingsForCurrentUser($pluginName)) {
  318. $target = sprintf('<a href="index.php%s#%s">',
  319. Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'pluginSettings')),
  320. $pluginName);
  321. $message .= ' ' . Piwik::translate('CorePluginsAdmin_ChangeSettingsPossible', array($target, '</a>'));
  322. }
  323. $notification = new Notification($message);
  324. $notification->raw = true;
  325. $notification->title = Piwik::translate('General_WellDone');
  326. $notification->context = Notification::CONTEXT_SUCCESS;
  327. Notification\Manager::notify('CorePluginsAdmin_PluginActivated', $notification);
  328. $this->redirectToIndex('CorePluginsAdmin', $actionToRedirect);
  329. }
  330. }
  331. public function deactivate($redirectAfter = true)
  332. {
  333. $pluginName = $this->initPluginModification(static::DEACTIVATE_NONCE);
  334. $this->dieIfPluginsAdminIsDisabled();
  335. \Piwik\Plugin\Manager::getInstance()->deactivatePlugin($pluginName);
  336. $this->redirectAfterModification($redirectAfter);
  337. }
  338. public function uninstall($redirectAfter = true)
  339. {
  340. $pluginName = $this->initPluginModification(static::UNINSTALL_NONCE);
  341. $this->dieIfPluginsAdminIsDisabled();
  342. $uninstalled = \Piwik\Plugin\Manager::getInstance()->uninstallPlugin($pluginName);
  343. if (!$uninstalled) {
  344. $path = Filesystem::getPathToPiwikRoot() . '/plugins/' . $pluginName . '/';
  345. $messagePermissions = Filechecks::getErrorMessageMissingPermissions($path);
  346. $messageIntro = Piwik::translate("Warning: \"%s\" could not be uninstalled. Piwik did not have enough permission to delete the files in $path. ",
  347. $pluginName);
  348. $exitMessage = $messageIntro . "<br/><br/>" . $messagePermissions;
  349. $exitMessage .= "<br> Or manually delete this directory (using FTP or SSH access)";
  350. Piwik_ExitWithMessage($exitMessage, $optionalTrace = false, $optionalLinks = false, $optionalLinkBack = true);
  351. }
  352. $this->redirectAfterModification($redirectAfter);
  353. }
  354. protected function initPluginModification($nonceName)
  355. {
  356. Piwik::checkUserHasSuperUserAccess();
  357. $nonce = Common::getRequestVar('nonce', null, 'string');
  358. if (!Nonce::verifyNonce($nonceName, $nonce)) {
  359. throw new \Exception(Piwik::translate('General_ExceptionNonceMismatch'));
  360. }
  361. Nonce::discardNonce($nonceName);
  362. $pluginName = Common::getRequestVar('pluginName', null, 'string');
  363. return $pluginName;
  364. }
  365. protected function redirectAfterModification($redirectAfter)
  366. {
  367. if ($redirectAfter) {
  368. Url::redirectToReferrer();
  369. }
  370. }
  371. private function getPluginNamesHavingSettingsForCurrentUser()
  372. {
  373. return array_keys(SettingsManager::getPluginSettingsForCurrentUser());
  374. }
  375. }