Revision: 2219
Author: andreassolberg
Date: Mon Mar 15 06:55:48 2010
Log: add module.php CLI to install modules
http://code.google.com/p/simplesamlphp/source/detail?r=2219

Added:
 /trunk/bin/module.php
 /trunk/modules/core/lib/ModuleDefinition.php
 /trunk/modules/core/lib/ModuleInstaller.php
Modified:
 /trunk/modules/modinfo/dictionaries/modinfo.definition.json
 /trunk/modules/modinfo/templates/modlist.php
 /trunk/modules/modinfo/www/index.php
 /trunk/www/resources/default.css

=======================================
--- /dev/null
+++ /trunk/bin/module.php       Mon Mar 15 06:55:48 2010
@@ -0,0 +1,77 @@
+#!/usr/bin/env php
+<?php
+
+/* This is the base directory of the simpleSAMLphp installation. */
+$baseDir = dirname(dirname(__FILE__));
+
+/* Add library autoloader. */
+require_once($baseDir . '/lib/_autoload.php');
+
+if (count($argv) < 1) {
+ echo "Wrong number of parameters. Run: " . $argv[0] . " [install,show] url [branch]\n"; exit;
+}
+
+// Needed in order to make session_start to be called before output is printed.
+$session = SimpleSAML_Session::getInstance();
+$config = SimpleSAML_Configuration::getConfig('config.php');
+
+
+$action = $argv[1];
+
+
+function getModinfo() {
+       global $argv;
+       if (count($argv) < 2)
+               throw new Exception('Missing second parameter: URL/ID');
+       return sspmod_core_ModuleDefinition::load($argv[2]);
+}
+
+function getBranch() {
+       global $argv;
+       if (isset($argv[3])) return $argv[3];
+       return NULL;
+}
+
+switch($action) {
+       case 'install':
+               $mod = getModinfo();
+               $installer = new sspmod_core_ModuleInstaller($mod);
+               $installer->install(getBranch());
+               break;
+
+       case 'remove':
+               $mod = getModinfo();
+               $installer = new sspmod_core_ModuleInstaller($mod);
+               $installer->remove(getBranch());
+               break;
+
+       case 'upgrade':
+               $mod = getModinfo();
+               $installer = new sspmod_core_ModuleInstaller($mod);
+               $installer->upgrade(getBranch());
+               break;
+
+       case 'upgrade-all' :
+               $mdir = scandir($config->getBaseDir() . 'modules/');
+               foreach($mdir AS $md) {
+                       if (!sspmod_core_ModuleDefinition::validId($md)) 
continue;
+                       if (!sspmod_core_ModuleDefinition::isDefined($md)) 
continue;
+                       $moduledef = sspmod_core_ModuleDefinition::load($md, 
'remote');
+                       $installer = new 
sspmod_core_ModuleInstaller($moduledef);
+
+                       if ($moduledef->updateExists() || 
$moduledef->alwaysUpdate()) {
+                               echo "Upgrading [" . $md . "]\n";
+                               $installer->upgrade();
+                       } else {
+                               echo "No updates available for [" . $md . "]\n";
+                       }
+               }
+               break;
+
+       default:
+               throw new Exception('Unknown action [' . $action . ']');
+}
+
+
+
+
=======================================
--- /dev/null
+++ /trunk/modules/core/lib/ModuleDefinition.php        Mon Mar 15 06:55:48 2010
@@ -0,0 +1,122 @@
+<?php
+
+class sspmod_core_ModuleDefinition {
+
+       public $def;
+       private static $cache;
+
+       private function __construct($def) {
+               $this->def = $def;
+               $this->requireValidIdentifier();
+       }
+
+       public static function validId($id) {
+               return preg_match('|^[a-zA-Z_]+$|', $id);
+       }
+
+       public static function isDefined($id) {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+               $filename = $basedir . 'modules/' . $id . '/definition.json';
+               return (file_exists($filename));
+       }
+
+       public static function load($id, $force = NULL) {
+
+               if (isset($cache[$id])) return $cache[$id];
+
+               if (self::validId($id)) {
+                       $config = 
SimpleSAML_Configuration::getConfig('config.php');
+                       $basedir = $config->getBaseDir();
+                       $filename = $basedir . 'modules/' . $id . 
'/definition.json';
+                       if (!file_exists($filename))
+ throw new Exception('Could not read definition file for module [' . $id . '] : ' . $filename);
+                       $defraw = file_get_contents($filename);
+                       $def = json_decode($defraw, TRUE);
+
+               } elseif(preg_match('|^http(s)?://.*$|', $id)) {
+                       $defraw = file_get_contents($id);
+                       $def = json_decode($defraw, TRUE);
+               } else {
+ throw new Exception('Could not resolve [' . $id . '] as URL nor module identifier.');
+               }
+               $cache[$id] = new sspmod_core_ModuleDefinition($def);
+
+
+
+               if (isset($force)) {
+                       if ($force === 'local') {
+                               if(preg_match('|^http(s)?://.*$|', $id)) {
+                                       return self::load($def['id']);
+                               }
+                       } elseif($force === 'remote') {
+                               if (self::validId($id)) {
+                                       if (!isset($def['definition']))
+ throw new Exception('Could not load remote definition file for module [' . $id . ']');
+                                       return self::load($def['definition']);
+                               }
+                       }
+               }
+
+               return $cache[$id];
+       }
+
+       private function requireValidIdentifier() {
+               if (!isset($this->def['id']))
+                       throw new Exception('Missing [id] value in module 
definition');
+               if (!preg_match('|^[a-zA-Z_]+$|', $this->def['id']))
+                       throw new Exception('Illegal characters in [id] in 
module definition');
+       }
+
+       public function getVersion($branch = NULL) {
+ if (!isset($this->def['access'])) throw new Exception('Missing [access] statement in module definition'); + if (!isset($this->def['branch'])) throw new Exception('Missing [branch] statement in module definition');
+
+               if (is_null($branch)) $branch = $this->def['branch'];
+
+ if (!isset($this->def['access'][$branch])) throw new Exception('Missing [access] information for branch [' . var_export($branch, TRUE) . ']'); + if (!isset($this->def['access'][$branch]['version'])) throw new Exception('Missing version information in [access] in branch [' . var_export($branch, TRUE) . ']');
+
+               return $this->def['access'][$branch]['version'];
+       }
+
+       public function alwaysUpdate($branch = NULL) {
+               $access = $this->getAccess($branch);
+               if ($access['type'] === 'svn') return TRUE;
+               return FALSE;
+       }
+
+       public function getBranch($branch = NULL) {
+ if (!isset($this->def['branch'])) throw new Exception('Missing [branch] statement in module definition');
+               if (is_null($branch)) $branch = $this->def['branch'];
+               return $branch;
+       }
+
+       public function updateExists($branch = NULL) {
+               $branch = $this->getBranch($branch);
+
+               $localDef = self::load($this->def['id'], 'local');
+               $thisVersion = $localDef->getVersion($branch);
+
+               $remoteDef = self::load($this->def['definition'], 'remote');
+               $remoteVersion = $remoteDef->getVersion($branch);
+
+ #echo ' Comparing versions localĀ [' . $thisVersion . '] and remote [' . $remoteVersion . ']' . "\n";
+
+               return version_compare($remoteVersion, $thisVersion, '>');
+       }
+
+
+       public function getAccess($branch = NULL) {
+ if (!isset($this->def['access'])) throw new Exception('Missing [access] statement in module definition'); + if (!isset($this->def['branch'])) throw new Exception('Missing [branch] statement in module definition');
+
+               if (is_null($branch)) $branch = $this->def['branch'];
+
+ if (!isset($this->def['access'][$branch])) throw new Exception('Missing [access] information for branch [' . var_export($branch, TRUE) . ']');
+
+               return $this->def['access'][$branch];
+       }
+
+
+}
=======================================
--- /dev/null
+++ /trunk/modules/core/lib/ModuleInstaller.php Mon Mar 15 06:55:48 2010
@@ -0,0 +1,188 @@
+<?php
+
+class sspmod_core_ModuleInstaller {
+
+       public $module;
+
+       public function __construct(sspmod_core_ModuleDefinition $module) {
+               $this->module = $module;
+
+       }
+
+       public function remove($branch = NULL) {
+               $access = $this->module->getAccess($branch);
+
+               switch($access['type']) {
+                       // case 'svn' :
+                       //      $this->requireInstalled();
+                       //      $this->remove($access);
+                       //      break;
+
+                       default:
+                               $this->requireInstalled();
+                               $this->removeModuleDir($access);
+                               break;
+
+               }
+       }
+
+       public function install($branch = NULL) {
+
+               $access = $this->module->getAccess($branch);
+
+               switch($access['type']) {
+                       case 'svn' :
+                               $this->requireNotInstalled();
+                               $this->svnCheckout($access);
+                               $this->enable();
+                               $this->prepareConfig();
+                               break;
+
+                       case 'zip' :
+                               $this->requireNotInstalled();
+                               $this->zipLoad($access);
+                               $this->enable();
+                               $this->prepareConfig();
+                               break;
+
+
+                       default:
+ throw new Exception('Unknown access method type. Not one of [zip,tgz,svn]');
+
+               }
+
+       }
+
+       public static function exec($cmd) {
+               echo ' $ ' . $cmd . "\n";
+               $output = shell_exec(escapeshellcmd($cmd));
+
+               if (empty($output)) return;
+
+               $oa = explode("\n", $output);
+
+               foreach($oa AS $ol) {
+                       echo ' > ' . $ol . "\n";
+               }
+
+       }
+
+       public function upgrade($branch = NULL) {
+
+               $access = $this->module->getAccess($branch);
+
+               switch($access['type']) {
+                       case 'svn' :
+                               $this->requireInstalled();
+                               $this->svnUpdate($access);
+                               $this->enable();
+                               $this->prepareConfig();
+                               break;
+
+                       case 'zip' :
+                               $this->requireInstalled();
+                               $this->zipLoad($access);
+                               $this->enable();
+                               $this->prepareConfig();
+                               break;
+
+                       default:
+ throw new Exception('Unknown access method type. Not one of [zip,tgz,svn]');
+
+               }
+
+       }
+
+       public function dirExists() {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+
+               $dir = $basedir . 'modules/' . $this->module->def['id'];
+
+               return (file_exists($dir) && is_dir($dir));
+       }
+
+       public function requireValidURL($url) {
+               if (!preg_match('|http(s)?://[a-zA-Z0-9_-/.]|', $url))
+                       throw new Exception('Invalid URL [' . $url . ']');
+       }
+
+       public function requireNotInstalled() {
+               if ($this->dirExists())
+ throw new Exception('The module [' . $this->module->def['id'] . '] is already installed.');
+       }
+
+       public function requireInstalled() {
+               if (!$this->dirExists())
+ throw new Exception('The module [' . $this->module->def['id'] . '] is not installed.');
+       }
+
+       public function svnCheckout($access) {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+ $cmd = "svn co " . escapeshellarg($access['url']) . " " . $basedir . "modules/" . $this->module->def['id'];
+               self::exec($cmd);
+       }
+
+       public function svnUpdate($access) {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+               $cmd = "svn up " . $basedir . "modules/" . 
$this->module->def['id'];
+               self::exec($cmd);
+       }
+
+       public function removeModuleDir($access) {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+               $cmd = "rm -rf " . $basedir . "modules/" . 
$this->module->def['id'];
+               self::exec($cmd);
+       }
+
+       public function enable() {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+
+               $this->requireInstalled();
+
+ $cmd = "touch " . $basedir . "modules/" . $this->module->def['id'] . '/enable';
+               self::exec($cmd);
+       }
+
+       public function prepareConfig() {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+
+               $this->requireInstalled();
+
+ $dir = $basedir . "modules/" . $this->module->def['id'] . '/config-templates';
+               if (!file_exists($dir)) return;
+
+               $files = scandir($dir);
+               foreach($files AS $file) {
+                       if(!preg_match('|^.*\.php|', $file)) continue;
+
+                       if (file_exists($basedir . 'config/' . $file)) {
+ echo "Configuration file [" . $file . "] already exists. Will not overwrite existing file.\n";
+                               continue;
+                       }
+
+                       $cmd = 'cp ' . $dir . '/' . $file . ' ' . $basedir . 
'config/';
+                       self::exec($cmd);
+               }
+       }
+
+       public function zipLoad($access) {
+               $config = SimpleSAML_Configuration::getConfig('config.php');
+               $basedir = $config->getBaseDir();
+
+               $zipfile = $access['url'];
+               $localfile = tempnam(sys_get_temp_dir(), 'ssp-module-');
+               $filecontents = file_get_contents($zipfile);
+               file_put_contents($localfile, $filecontents);
+
+ $cmd = "unzip -qo " . escapeshellarg($localfile) . " -d " . $basedir . "modules/";
+               self::exec($cmd);
+
+       }
+
+}
=======================================
--- /trunk/modules/modinfo/dictionaries/modinfo.definition.json Tue Jan 12 03:33:22 2010 +++ /trunk/modules/modinfo/dictionaries/modinfo.definition.json Mon Mar 15 06:55:48 2010
@@ -13,5 +13,15 @@
        },
        "modlist_disabled": {
                "en": "Disabled"
-       }
-}
+       },
+       "latest_version" : {
+               "en" : "Updated"
+       },
+       "update_exists" : {
+               "en" : "Update available"
+       },
+       "version" : {
+               "en" : "Version"
+       }
+
+}
=======================================
--- /trunk/modules/modinfo/templates/modlist.php        Wed Jan 13 04:17:03 2010
+++ /trunk/modules/modinfo/templates/modlist.php        Mon Mar 15 06:55:48 2010
@@ -10,15 +10,29 @@

 <h2><?php echo($this->data['header']); ?></h2>

-<table>
+<table class="modules" style="width: 100%">
 <tr>
-<th><?php echo($this->t('{modinfo:modinfo:modlist_name}')); ?></th>
-<th><?php echo($this->t('{modinfo:modinfo:modlist_status}')); ?></th>
+<th colspan="2"><?php echo($this->t('{modinfo:modinfo:modlist_name}')); ?></th>
+<th ><?php echo($this->t('{modinfo:modinfo:modlist_status}')); ?></th>
+<th colspan="2"><?php echo($this->t('{modinfo:modinfo:version}')); ?></th>
 </tr>
 <?php
+
+$i = 0;
 foreach($this->data['modules'] as $id => $info) {
-       echo('<tr>');
-       echo('<td>' . htmlspecialchars($id) . '</td>');
+       echo('<tr class="' . ($i++ % 2 == 0 ? 'odd' : 'even') . '">');
+
+
+       if (isset($info['def'])) {
+ echo('<td><a href="http://simplesamlphp.org/modules/' . htmlspecialchars($id) . '">' . htmlspecialchars($info['def']->def['name']) . '</a></td>');
+       } else {
+               echo('<td> </td>');
+       }
+
+
+       echo('<td><tt>' . htmlspecialchars($id) . '</tt></td>');
+
+
        if($info['enabled']) {
echo('<td><img src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/accept.png" alt="' . htmlspecialchars($this->t('{modinfo:modinfo:modlist_enabled}')) . '" /></td>');
@@ -26,6 +40,22 @@
echo('<td><img src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/delete.png" alt="' . htmlspecialchars($this->t('{modinfo:modinfo:modlist_disabled}')) . '" /></td>');
        }
+
+       if (isset($info['def'])) {
+ echo('<td>' . htmlspecialchars($info['def']->getVersion()) . ' (' .htmlspecialchars($info['def']->getBranch()) . ')</td>');
+               if ($info['def']->updateExists()) {
+ echo('<td><img style="display: inline" src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/delete.png" alt="' . + htmlspecialchars($this->t('{modinfo:modinfo:update_exists}')) . '" /> ' . + htmlspecialchars($this->t('{modinfo:modinfo:update_exists}')) . '</td>');
+               } else {
+ echo('<td><img style="display: inline" src="/' . $this->data['baseurlpath'] . 'resources/icons/silk/accept.png" alt="' . + htmlspecialchars($this->t('{modinfo:modinfo:latest_version}')) . '" /> ' . + htmlspecialchars($this->t('{modinfo:modinfo:latest_version}')) . '</td>');
+               }
+       } else {
+               echo('<td colspan="2"> </td>');
+       }
+
        echo('</tr>');
 }
 ?>
=======================================
--- /trunk/modules/modinfo/www/index.php        Fri Nov 14 13:55:18 2008
+++ /trunk/modules/modinfo/www/index.php        Mon Mar 15 06:55:48 2010
@@ -9,8 +9,20 @@
        $modinfo[$m] = array(
                'enabled' => SimpleSAML_Module::isModuleEnabled($m),
        );
+       if (sspmod_core_ModuleDefinition::isDefined($m)) {
+               $modinfo[$m]['def'] = sspmod_core_ModuleDefinition::load($m);
+       }
+
 }

+function cmpa($a, $b) {
+
+    if (isset($a['def']) && !isset($b['def'])) return -1;
+    if (isset($b['def']) && !isset($a['def'])) return 1;
+       return 0;
+}
+uasort($modinfo, 'cmpa');
+
 $config = SimpleSAML_Configuration::getInstance();
 $t = new SimpleSAML_XHTML_Template($config, 'modinfo:modlist.php');
 $t->data['modules'] = $modinfo;
=======================================
--- /trunk/www/resources/default.css    Fri Jan 29 04:59:30 2010
+++ /trunk/www/resources/default.css    Mon Mar 15 06:55:48 2010
@@ -276,6 +276,15 @@
        padding: 2px 2em 2px 2em;
 }

+table.modules {
+       border-collapse: collapse;
+}
+table.modules tr td {
+       border-bottom: 1px solid #ddd;
+}
+table.modules tr.even td {
+       background: #f0f0f0;
+}

 /* Attribute presentation in example page */
 table.attributes {

--
You received this message because you are subscribed to the Google Groups 
"simpleSAMLphp commits" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/simplesamlphp-commits?hl=en.

Reply via email to