#!/usr/bin/php update-gosa - class cache updated and plugin manager for GOsa Usage: update-gosa install dsc Install the plugin using the dsc information placed in the plugin source directory. update-gosa remove plugin Remove the plugin named "plugin" from the current configuration. update-gosa list Lists installed plugins update-gosa rescan-i18n Rebuilds the translations update-gosa rescan-images Rebuilds the themes master image update-gosa rescan-classes Rebuilds the class list update-gosa Shortcut for rescan-classes and rescan-i18n read())) { if ($entry[0] != '.') { $themes[]= basename($entry); } } $d->close(); return $themes; } /* Function to include all class_ files starting at a given directory base */ function get_classes($folder= ".") { static $base_dir= ""; static $result= array(); if ($base_dir == ""){ if ($folder == "."){ $base_dir= getcwd(); } else { $base_dir= $folder; } } $currdir=getcwd(); if ($folder){ chdir("$folder"); } $dh = opendir("."); while(is_resource($dh) && false !== ($file = readdir($dh))){ if (preg_match("/.*\.svn.*/", $file) || preg_match("/.*smarty.*/i",$file) || preg_match("/.*\.tpl.*/",$file) || ($file==".") ||($file =="..")){ continue; } /* Recurse through all "common" directories */ if (is_dir($file) && !file_exists("{$file}/excludeFromAutoLoad")){ get_classes($file); continue; } /* Only take care about .inc and .php files... */ if (!(preg_match('/\.php$/', $file) || preg_match('/\.inc$/', $file))){ continue; } /* Include existing class_ files */ $contents= file($file); foreach($contents as $line){ $line= chop($line); if (preg_match('/^\s*class\s*\w.*$/', $line)){ $class= preg_replace('/^\s*class\s*(\w+).*$/', '\1', $line); $result[$class]= preg_replace("%$base_dir/%", "", "$currdir/$folder/$file"); } } } @closedir($dh); chdir($currdir); return ($result); } function rescan_classes() { echo "Updating class cache...\n"; $class_mapping= get_classes(); $filename= GOSA_HOME."/include/class_location.inc"; /* Sanity checks */ if (!file_exists($filename) || is_writable($filename)) { if (!$handle= fopen($filename, 'w')) { echo "Cannot open file \"$filename\" - aborted\n"; exit (1); } } else { echo "File \"$filename\" is not writable - aborted\n"; exit (2); } fwrite ($handle, " $value){ fwrite ($handle, " \"$key\" => \"$value\",\n"); } fwrite ($handle, " );\n"); fclose($handle); } function rescan_i18n() { echo "Updating internationalization...\n"; $languages= array(); $size= strlen(LOCALE_DIR); /* Get all available messages.po files, sort them for languages */ $dir= new RecursiveDirectoryIterator(LOCALE_DIR); $all= new RecursiveIteratorIterator($dir); foreach ( $all as $element ){ if ($element->isFile() && preg_match('/\/LC_MESSAGES\/messages.po$/', $element->getPathname())){ $lang= preg_replace('/^.*\/([^\/]+)\/LC_MESSAGES\/.*$/', '\1', $element); if (!isset($languages[$lang])){ $languages[$lang]= array(); } $languages[$lang][]= substr($element->getPathName(), $size+1); } } /* For each language, merge the target .mo to the compiled directory. */ foreach ($languages as $language => $po_files){ if (!is_dir(LOCALE_DIR."/compiled/${language}/LC_MESSAGES")){ if (!mkdir (LOCALE_DIR."/compiled/${language}/LC_MESSAGES", 0755, TRUE)){ echo "Failed to create '".LOCALE_DIR."/compiled/${language}/LC_MESSAGES'- aborted"; exit (3); } } /* Cat all these po files into one single file */ system ("(cd ".LOCALE_DIR." && msgcat --use-first ".implode(" ", $po_files)." > compiled/${language}/LC_MESSAGES/messages.po)", $val); if ($val != 0){ echo "Merging of message files failed - aborted"; exit (4); } system ("(cd ".LOCALE_DIR."/compiled/${language}/LC_MESSAGES && msgfmt -o messages.mo messages.po && rm messages.po)", $val); if ($val != 0){ echo "Compiling of message files failed - aborted"; exit (5); } } echo "! Warning: you may need to reload your webservice!\n"; } function rescan_guide() { $master_guide= "doc/guide.xml"; echo "Updating Online Help Index...\n"; $master_guide_content="\n". "\n\n". "\n\n". "\n". "\n"; $guide= 'doc/core/guide.xml'; if(file_exists($guide) && is_readable($guide)) { $master_guide_content.= file_get_contents($guide); } if(file_exists('doc/plugins')) { $plugins= scandir('doc/plugins'); foreach($plugins as $key => $plugin) { if($plugin != '.' && $plugin != '..') { if(is_dir('doc/plugins/'.$plugin)) { $guide= 'doc/plugins/'.$plugin.'/guide.xml'; if(file_exists($guide) && is_readable($guide)) { $master_guide_content.= file_get_contents($guide); } } } } } $master_guide_content.= ""; $master_guide_content= preg_replace("/[ \t][ \t]*/", " ", $master_guide_content); if((file_exists($master_guide) && is_writable($master_guide)) || is_writable('doc')) { file_put_contents($master_guide, $master_guide_content); } } function parse_ini($file) { global $description, $provides, $depends, $versions, $conflicts; $res= ""; if (file_exists($file)){ $tmp= parse_ini_file($file, TRUE); if (isset($tmp['gosa-plugin'])){ $plugin= &$tmp['gosa-plugin']; if (isset($plugin['name'])&& isset($plugin['description'])){ $res= $plugin['name']; $description[$res]= $plugin['description']; $versions[$res]= $plugin['version']; $provides[$res]= $res; if (isset($plugin['depends'])){ $depends[$res]= explode(',', preg_replace('/\s+/', '', $plugin['depends'])); } if (isset($plugin['conflicts'])){ $conflicts[$res]= explode(',', preg_replace('/\s+/', '', $plugin['conflicts'])); } } } } return $res; } function dependency_check() { global $description, $provides, $depends; foreach ($depends as $name => $pl_depends){ foreach ($pl_depends as $pl){ if (!in_array($pl, $provides)){ echo "! Error: plugin '$name' depends on '$pl' which is not provided by any plugin\n\n"; exit (1); } } } } function load_plugins() { if (!is_dir(PLUGSTATE_DIR)){ if (!mkdir (PLUGSTATE_DIR, 0755, TRUE)){ echo "Cannot create plugstate dir '".PLUGSTATE_DIR."' - aborted\n"; exit (2); } } $dir= new DirectoryIterator(PLUGSTATE_DIR); foreach ($dir as $entry){ if ($dir->isDir() && !preg_match('/^\./', $dir->__toString())){ $file= $dir->getPathName()."/plugin.dsc"; if (parse_ini($file) == ""){ echo "! Warning: plugin ".$dir->getPathName()." is missing declarations\n"; } } } } function list_plugins() { global $description, $versions; $count= 0; /* Load plugin list */ load_plugins(); /* Show plugins */ foreach ($description as $name => $dsc){ if ($count == 0){ echo "Plugin\t\t|Version |Description\n"; echo "----------------------------------------------------------------------------\n"; } $ver= $versions[$name]; echo "$name\t\t|$ver\t |$dsc\n"; $count++; } /* Yell about non existing plugins... */ if ($count == 0){ echo "No plugins found...\n\n"; } else { # Check for dependencies dependency_check(); echo "\n"; } } function install_plugin($file) { global $description, $provides, $depends, $conflicts; /* Load plugin list */ load_plugins(); /* Load .dsc file */ if (file_exists($file)){ $tmp= parse_ini_file($file, TRUE); if (isset($tmp['gosa-plugin'])){ $plugin= &$tmp['gosa-plugin']; if (isset($plugin['name'])&& isset($plugin['description'])){ $name= $plugin['name']; $description= $plugin['description']; $depends= array(); if (isset($plugin['depends'])){ $depends= explode(',', preg_replace('/\s+/', '', $plugin['depends'])); } /* Already installed? */ if (isset($provides[$name])){ echo "! Error: plugin already installed\n\n"; exit (3); } /* Check if dependencies are fullfilled */ foreach ($depends as $dep){ $found= false; foreach ($provides as $provide => $dummy){ if ($dep == $provide){ $found= true; break; } } if (!$found){ echo "! Error: plugin depends on '$dep', but this is not installed\n\n"; exit (3); } } /* Check for conflicts */ foreach ($conflicts as $conf){ if (!in_array($conf, $provides)){ echo "! Warning: plugin conflicts with '$conf'\n\n"; } } /* Create plugstate directory and touch plugin.lst */ if (!mkdir (PLUGSTATE_DIR."/$name", 0755, TRUE)){ echo "Failed to create '".PLUGSTATE_DIR."/$name - aborted"; exit (3); } if (!$handle= fopen(PLUGSTATE_DIR."/$name/plugin.lst", 'w')) { echo "Cannot open file '$filename' - aborted\n"; exit (1); } echo "Installing plugin '$name'...\n"; /* Copy and fill plugin.lst */ $path= dirname($file); $dir= new RecursiveDirectoryIterator($path); $all= new RecursiveIteratorIterator($dir); foreach ( $all as $entry ){ $source= $path."/".substr($entry->getPathName(), strlen($path) + 1); /* Skip description - it belongs to the state dir */ if (preg_match('/\/plugin.dsc$/', $source)){ copy ($source, PLUGSTATE_DIR."/$name/plugin.dsc"); continue; } /* Skip well known directories */ if (preg_match('/^\.+$/', $source) || preg_match('/\/\.svn\//', $source)) { continue; } /* Calculate destination */ if (preg_match("%^.*locale/%", $source)){ $dest= GOSA_HOME."/locale/plugins/$name/".preg_replace("%^.*locale/%", "", $source); } elseif (preg_match("%^.*help/%", $source)) { $dest= GOSA_HOME."/doc/plugins/$name/".preg_replace("%^.*help/%", "", $source); } elseif (preg_match("%^.*html/%", $source)) { $dest= GOSA_HOME."/html/plugins/$name/".preg_replace("%^.*html/%", "", $source); } else { $dest= GOSA_HOME."/plugins/".substr($entry->getPathName(), strlen($path) + 1); } /* Destination exists in case of directories? */ if ($entry->isDir()){ if (!is_dir($dest)){ mkdir($dest, 0755, TRUE); fwrite ($handle, "$dest\n"); } } else { if (!is_dir(dirname($dest))){ mkdir(dirname($dest), 0755, TRUE); fwrite ($handle, dirname($dest)."\n"); } } /* Copy files */ if ($entry->isFile()){ copy ($source, $dest); } /* Note what we did... */ fwrite ($handle, "$dest\n"); } fclose($handle); } } } /* Update caches */ rescan_classes(); rescan_i18n(); rescan_guide(); } function remove_plugin($name) { global $description, $depends; /* Load plugin list */ load_plugins(); /* Present? */ if (!isset($description[$name])){ echo "! Error: cannot find a plugin named '$name'\n\n"; exit (1); } /* Depends? */ foreach ($depends as $sname => $pl_depends){ if (in_array($name, $pl_depends)){ echo "! Error: plugin '$sname' depends on '$name' - cannot remove it\n\n"; exit (1); } } /* Load information */ if (!file_exists(PLUGSTATE_DIR."/$name/plugin.lst")){ echo "! Error: cannot remove plugin '$name' - no install history found\n\n"; exit (1); } echo "Removing plugin '$name'...\n"; $contents= file(PLUGSTATE_DIR."/$name/plugin.lst"); $cnv= array(); foreach($contents as $line){ $entry= chop($line); $cnv[strlen($entry).":$entry"]= $entry; } krsort($cnv); /* Remove files first */ clearstatcache(); foreach ($cnv as $entry){ if (is_dir($entry)){ rmdir($entry); continue; } if (file_exists($entry)){ unlink($entry); } } /* Remove state directory for plugin */ rmdirRecursive(PLUGSTATE_DIR."/$name"); /* Update caches */ rescan_classes(); rescan_i18n(); rescan_guide(); } function rescan_images($path, $theme) { $widths= array(); $heights= array(); $paths= array(); $posX= array(); $posY= array(); $baseLength= strlen($path); $heightStats= array(); $warnings= array(); $checksums= array(); $styles= array(); $duplicates= array(); echo "Updating master image for theme '$theme'..."; // Check for image magick convert if (!function_exists("imageFilter")){ exec("which convert", $res, $ret); if ($ret != 0) { die("Your system has no bundled gd support for imageFilter function. Please install imagemagick in order to use an external command.\n"); } } // Scan for images in the given path flush(); foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $fileInfo) { // We're only interested in png files $indexPath= substr($fileInfo->getPathname(), $baseLength + 1); $path= $fileInfo->getPathname(); if (preg_match('/\.png$/', $indexPath) && !preg_match('/\.svn/', $path) && !preg_match('/themes\/[^\/]+\/images\/img.png$/', $path)){ // Grey image if it is not already one if (preg_match('/grey/', $indexPath)) { echo "!"; $warnings[]= "! Warning: skipped possible *grey* image $path"; flush(); continue; } // New image if it is not already one if (preg_match('/new/', $indexPath) && !preg_match('/new\.png$/', $indexPath)) { echo "!"; $warnings[]= "! Warning: skipped possible *new* image $path"; flush(); continue; } // Catch available themes if (preg_match('/themes\//', $indexPath) && !preg_match('/themes\/'.$theme.'\//', $indexPath)) { continue; } // Load image $img= imageCreateFromPng($path); $width= imageSX($img); $height= imageSY($img); imageDestroy($img); $greyIndexPath= preg_replace('/\.png$/', '-grey.png', $indexPath); // Is this image already there? $checksum= md5_file($path); if (in_array($checksum, $checksums)) { $warnings[]= "! Warning: images $indexPath seems to be a duplicate of ".array_search($checksum, $checksums); $duplicates[$indexPath]= array_search($checksum, $checksums); $duplicates[$greyIndexPath]= preg_replace('/\.png$/', '-grey.png', array_search($checksum, $checksums)); continue; } else { $checksums[$indexPath]= $checksum; } // Ordinary image $widths[$indexPath]= $width; $heights[$indexPath]= $height; $paths[$indexPath]= $path; // Grey image $widths[$greyIndexPath]= $width; $heights[$greyIndexPath]= $height; $paths[$greyIndexPath]= $path; // Feed height statistics if (!isset($heightStats[$height])) { $heightStats[$height]= 1; } else { $heightStats[$height]++; } } echo "."; flush(); } echo "\n"; // Do some stupid height calculation arsort($heightStats, SORT_NUMERIC); reset($heightStats); $popular= current($heightStats); krsort($heightStats); reset($heightStats); $max= current($heightStats); $maxHeight= (floor($max / $popular) + 1) * $popular * 6; // Sort to get biggest values arsort($widths, SORT_NUMERIC); reset($widths); echo "Calculating master image dimensions: "; flush(); // Build container image $cursorX= 0; $cursorY= 0; $colWidth= 0; $rowHeight= 0; $colX= 0; $colY= 0; $maxY= 0; $maxX= 0; // Walk thru width sorted images foreach ($widths as $imagePath => $imageWidth) { $imageHeight= $heights[$imagePath]; // First element in this column if ($colWidth == 0) { $colWidth= $imageWidth; } // First element in this row if ($rowHeight < $imageHeight) { $rowHeight= $imageHeight; } // Does the image match here? if ($cursorX + $imageWidth > $colX + $colWidth) { if ($cursorY + $imageHeight >= $maxHeight) { // Reached max height, move to the next column $colX+= $colWidth; $cursorX= $colX; $colWidth= $imageWidth; $rowHeight= $imageHeight; $colY= $cursorY= 0; } else { // Next row $colY+= $rowHeight; $cursorY= $colY; $cursorX= $colX; $rowHeight= $imageHeight; } $maxY=($colY + $imageHeight > $maxY)?$colY+$imageHeight:$maxY; } // Save calculated position $posX[$imagePath]= $cursorX; $posY[$imagePath]= $cursorY; // Move X cursor to the next position $cursorX+= $imageWidth; $maxX=($colX+$imageWidth > $maxX)?$colX+$imageWidth:$maxX; } // Print maximum dimensions echo $maxY."x".$maxX."\n"; echo "Processing"; flush(); // Create result image $dst= imageCreateTrueColor($maxX, $maxY); imageAlphaBlending($dst, true); $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127); imageFill($dst, 0, 0, $transparent); imageSaveAlpha($dst, true); // Finally assemble picture foreach ($heights as $imagePath => $imageHeight) { $imageWidth= $widths[$imagePath]; $x= $posX[$imagePath]; $y= $posY[$imagePath]; // Insert source image... // Eventually convert it to grey before if (preg_match('/-grey\.png$/', $imagePath)) { if (!function_exists("imageFilter")){ exec("convert ".$paths[$imagePath]." -colorspace Gray /tmp/grey-converted.png"); $src= imageCreateFromPng("/tmp/grey-converted.png"); } else { $src= imageCreateFromPng($paths[$imagePath]); imageFilter($src, IMG_FILTER_GRAYSCALE); } } else { $src= imageCreateFromPng($paths[$imagePath]); } // Merge image imageCopyResampled($dst, $src, $x, $y, 0, 0, $imageWidth, $imageHeight, $imageWidth, $imageHeight); imageDestroy($src); // Store style $styles[$imagePath]= "background-position:-".$x."px -".$y."px;width:".$imageWidth."px;height:".$imageHeight."px"; echo "."; flush(); } /* Add duplicates */ foreach ($duplicates as $imagePath => $realPath) { $styles[$imagePath]= $styles[$realPath]; } imagePNG($dst, GOSA_HOME."/html/themes/$theme/images/img.png", 9); imageDestroy($dst); // Show warnings images foreach ($warnings as $warn) { echo "$warn\n"; } // Write styles echo "Writing styles..."; $fp = fopen(GOSA_HOME."/ihtml/themes/$theme/img.styles", 'w'); fwrite($fp, serialize($styles)); fclose($fp); echo "\n"; } /* Fill global values */ $description= $provides= $depends= $versions= $conflicts= array(); /* Action specified? */ if ($argc < 2){ rescan_classes(); rescan_i18n(); rescan_guide(); foreach (get_themes() as $theme) { rescan_images(GOSA_HOME."/html", $theme); } exit (0); } switch ($argv[1]){ case 'install': if (isset($argv[2])){ install_plugin($argv[2]); } else { echo "Usage: update-gosa install dsc-file\n\n"; exit (1); } break; case 'list': list_plugins(); break; case 'remove': if (isset($argv[2])){ remove_plugin($argv[2]); } else { echo "Usage: update-gosa remove plugin-name\n\n"; exit (1); } break; case 'rescan-i18n': rescan_i18n(); break; case 'rescan-classes': rescan_classes(); break; case 'rescan-images': foreach (get_themes() as $theme) { rescan_images("html", $theme); } break; default: echo "Error: Supplied command not known\n\n"; print_usage(); break; } ?>