Open Monograph Press  3.3.0
PluginHelper.inc.php
1 <?php
2 
16 import('lib.pkp.classes.site.Version');
17 import('lib.pkp.classes.site.VersionCheck');
18 import('lib.pkp.classes.file.FileManager');
19 import('classes.install.Install');
20 import('classes.install.Upgrade');
21 
22 define('PLUGIN_ACTION_UPLOAD', 'upload');
23 define('PLUGIN_ACTION_UPGRADE', 'upgrade');
24 
25 define('PLUGIN_VERSION_FILE', 'version.xml');
26 define('PLUGIN_INSTALL_FILE', 'install.xml');
27 define('PLUGIN_UPGRADE_FILE', 'upgrade.xml');
28 
29 class PluginHelper {
36  public function extractPlugin($filePath, $originalFileName) {
37  $fileManager = new FileManager();
38  // tar archive basename (less potential version number) must
39  // equal plugin directory name and plugin files must be in a
40  // directory named after the plug-in (potentially with version)
41  $matches = array();
42  PKPString::regexp_match_get('/^[a-zA-Z0-9]+/', basename($originalFileName, '.tar.gz'), $matches);
43  $pluginShortName = array_pop($matches);
44  if (!$pluginShortName) {
45  throw new Exception(__('manager.plugins.invalidPluginArchive'));
46  }
47 
48  // Create random dirname to avoid symlink attacks.
49  $pluginExtractDir = dirname($filePath) . DIRECTORY_SEPARATOR . $pluginShortName . substr(md5(mt_rand()), 0, 10);
50  if (!mkdir($pluginExtractDir)) throw new Exception('Could not create directory ' . $pluginExtractDir);
51 
52  // Test whether the tar binary is available for the export to work
53  $tarBinary = Config::getVar('cli', 'tar');
54  if (empty($tarBinary) || !file_exists($tarBinary)) {
55  rmdir($pluginExtractDir);
56  throw new Exception(__('manager.plugins.tarCommandNotFound'));
57  }
58 
59  $output = '';
60  $returnCode = 0;
61  if (in_array('exec', explode(',', ini_get('disable_functions')))) throw new Exception('The "exec" PHP function has been disabled on your server. Contact your system adminstrator to enable it.');
62  exec($tarBinary.' -xzf ' . escapeshellarg($filePath) . ' -C ' . escapeshellarg($pluginExtractDir), $output, $returnCode);
63  if ($returnCode) {
64  $fileManager->rmtree($pluginExtractDir);
65  throw new Exception(__('form.dropzone.dictInvalidFileType'));
66  }
67 
68  // Look for a directory named after the plug-in's short
69  // (alphanumeric) name within the extracted archive.
70  if (is_dir($tryDir = $pluginExtractDir . '/' . $pluginShortName)) {
71  return $tryDir; // Success
72  }
73 
74  // Failing that, look for a directory named after the
75  // archive. (Typically also contains the version number
76  // e.g. with github generated release archives.)
77  PKPString::regexp_match_get('/^[a-zA-Z0-9.-]+/', basename($originalFileName, '.tar.gz'), $matches);
78  if (is_dir($tryDir = $pluginExtractDir . '/' . array_pop($matches))) {
79  // We found a directory named after the archive
80  // within the extracted archive. (Typically also
81  // contains the version number, e.g. github
82  // generated release archives.)
83  return $tryDir;
84  }
85 
86  // Could not match the plugin archive's contents against our expectations; error out.
87  $fileManager->rmtree($pluginExtractDir);
88  throw new Exception(__('manager.plugins.invalidPluginArchive'));
89  }
90 
96  public function installPlugin($path) {
97  $versionFile = $path . '/' . PLUGIN_VERSION_FILE;
98 
99  $pluginVersion = VersionCheck::getValidPluginVersionInfo($versionFile);
100 
101  $versionDao = DAORegistry::getDAO('VersionDAO');
102  $installedPlugin = $versionDao->getCurrentVersion($pluginVersion->getProductType(), $pluginVersion->getProduct(), true);
103  $pluginDest = Core::getBaseDir() . '/' . strtr($pluginVersion->getProductType(), '.', '/') . '/' . $pluginVersion->getProduct();
104 
105  if ($installedPlugin && file_exists($pluginDest)) {
106  if ($this->_checkIfNewer($pluginVersion->getProductType(), $pluginVersion->getProduct(), $pluginVersion)) {
107  throw new Exception(__('manager.plugins.pleaseUpgrade'));
108  } else {
109  throw new Exception(__('manager.plugins.installedVersionOlder'));
110  }
111  }
112 
113  // Copy the plug-in from the temporary folder to the target folder.
114  $fileManager = new FileManager();
115  if (!$fileManager->copyDir($path, $pluginDest)) throw new Exception('Could not copy plugin to desination!');
116  if (!$fileManager->rmtree($path)) throw new Exception('Could not remove temporary plugin path!');
117 
118  // Upgrade the database with the new plug-in.
119  $installFile = $pluginDest . '/' . PLUGIN_INSTALL_FILE;
120  if(!is_file($installFile)) $installFile = Core::getBaseDir() . '/' . PKP_LIB_PATH . '/xml/defaultPluginInstall.xml';
121  assert(is_file($installFile));
122  $siteDao = DAORegistry::getDAO('SiteDAO');
123  $site = $siteDao->getSite();
124  $params = $this->_getConnectionParams();
125  $params['locale'] = $site->getPrimaryLocale();
126  $params['additionalLocales'] = $site->getSupportedLocales();
127  $installer = new Install($params, $installFile, true);
128  $installer->setCurrentVersion($pluginVersion);
129  if (!$installer->execute()) {
130  // Roll back the copy
131  if (is_dir($pluginDest)) $fileManager->rmtree($pluginDest);
132  throw new Exception(__('manager.plugins.installFailed', array('errorString' => $installer->getErrorString())));
133  }
134 
135  $versionDao->insertVersion($pluginVersion, true);
136  return $pluginVersion;
137  }
138 
146  protected function _checkIfNewer($productType, $productName, $newVersion) {
147  $versionDao = DAORegistry::getDAO('VersionDAO');
148  $installedPlugin = $versionDao->getCurrentVersion($productType, $productName, true);
149  if ($installedPlugin && $installedPlugin->compare($newVersion) > 0) return true;
150  return false;
151  }
152 
157  protected function _getConnectionParams() {
158  return array(
159  'clientCharset' => Config::getVar('i18n', 'client_charset'),
160  'connectionCharset' => Config::getVar('i18n', 'connection_charset'),
161  'databaseDriver' => Config::getVar('database', 'driver'),
162  'databaseHost' => Config::getVar('database', 'host'),
163  'databaseUsername' => Config::getVar('database', 'username'),
164  'databasePassword' => Config::getVar('database', 'password'),
165  'databaseName' => Config::getVar('database', 'name')
166  );
167  }
168 
176  public function upgradePlugin($category, $plugin, $path) {
177  $fileManager = new FileManager();
178 
179  $versionFile = $path . '/' . PLUGIN_VERSION_FILE;
180  $pluginVersion = VersionCheck::getValidPluginVersionInfo($versionFile);
181 
182  // Check whether the uploaded plug-in fits the original plug-in.
183  if ('plugins.'.$category != $pluginVersion->getProductType()) {
184  throw new Exception(__('manager.plugins.wrongCategory'));
185  }
186 
187  if ($plugin != $pluginVersion->getProduct()) {
188  throw new Exception(__('manager.plugins.wrongName'));
189  }
190 
191  $versionDao = DAORegistry::getDAO('VersionDAO');
192  $installedPlugin = $versionDao->getCurrentVersion($pluginVersion->getProductType(), $pluginVersion->getProduct(), true);
193  if(!$installedPlugin) {
194  throw new Exception(__('manager.plugins.pleaseInstall'));
195  }
196 
197  if ($this->_checkIfNewer($pluginVersion->getProductType(), $pluginVersion->getProduct(), $pluginVersion)) {
198  throw new Exception(__('manager.plugins.installedVersionNewer'));
199  }
200 
201  $pluginDest = Core::getBaseDir() . '/plugins/' . $category . '/' . $plugin;
202 
203  // Delete existing files.
204  if (is_dir($pluginDest)) $fileManager->rmtree($pluginDest);
205 
206  // Check whether deleting has worked.
207  if(is_dir($pluginDest)) {
208  throw new Exception(__('manager.plugins.deleteError', array('pluginName' => $pluginVersion->getProduct())));
209  }
210 
211  // Copy the plug-in from the temporary folder to the target folder.
212  if (!$fileManager->copyDir($path, $pluginDest)) throw new Exception('Could not copy plugin to desination!');
213  if (!$fileManager->rmtree($path)) throw new Exception('Could not remove temporary plugin path!');
214 
215  $upgradeFile = $pluginDest . '/' . PLUGIN_UPGRADE_FILE;
216  if($fileManager->fileExists($upgradeFile)) {
217  $siteDao = DAORegistry::getDAO('SiteDAO');
218  $site = $siteDao->getSite();
219  $params = $this->_getConnectionParams();
220  $params['locale'] = $site->getPrimaryLocale();
221  $params['additionalLocales'] = $site->getSupportedLocales();
222  $installer = new Upgrade($params, $upgradeFile, true);
223 
224  if (!$installer->execute()) throw new Exception(__('manager.plugins.upgradeFailed', array('errorString' => $installer->getErrorString())));
225  }
226 
227  $installedPlugin->setCurrent(0);
228  $pluginVersion->setCurrent(1);
229  $versionDao->insertVersion($pluginVersion, true);
230  return $pluginVersion;
231  }
232 }
233 
PKPString\regexp_match_get
static regexp_match_get($pattern, $subject, &$matches)
Definition: PKPString.inc.php:256
PluginHelper\_getConnectionParams
_getConnectionParams()
Definition: PluginHelper.inc.php:157
PluginHelper\_checkIfNewer
_checkIfNewer($productType, $productName, $newVersion)
Definition: PluginHelper.inc.php:146
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
Upgrade
Perform system upgrade.
Definition: Upgrade.inc.php:19
PluginHelper\installPlugin
installPlugin($path)
Definition: PluginHelper.inc.php:96
Config\getVar
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
PluginHelper\upgradePlugin
upgradePlugin($category, $plugin, $path)
Definition: PluginHelper.inc.php:176
VersionCheck\getValidPluginVersionInfo
static getValidPluginVersionInfo($versionFile)
Definition: VersionCheck.inc.php:144
PluginHelper
Helper class implementing plugin administration functions.
Definition: PluginHelper.inc.php:29
Core\getBaseDir
static getBaseDir()
Definition: Core.inc.php:37
Install
Perform system installation.
Definition: Install.inc.php:28
PluginHelper\extractPlugin
extractPlugin($filePath, $originalFileName)
Definition: PluginHelper.inc.php:36
FileManager
Class defining basic operations for file management.
Definition: FileManager.inc.php:35