diff --git a/.gitignore b/.gitignore index b796d58b..6735ac1c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ login.txt *.swp gallery2-data mysqldata +modules/bulkexcelupload +themes/bootstrap_matrix +modules/mapv3/images/routes diff --git a/.htaccess-sample b/.htaccess-sample new file mode 100644 index 00000000..2dea77b9 --- /dev/null +++ b/.htaccess-sample @@ -0,0 +1,56 @@ +#{gallerySection} + +RewriteEngine On +# Redirect all requests to HTTPS +RewriteCond %{HTTPS} "!=on" +RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L] + +RedirectMatch 404 \.(md|txt|ini|Dockerfile|git|htaccess|inc) +RedirectMatch 404 (docker|config\.php|phpinfo|README|LICENSE) + +# BEGIN Url Rewrite section +# (Automatically generated. Do not edit this section) + + RewriteEngine On + + RewriteBase / + + RewriteCond %{REQUEST_FILENAME} -f [OR] + RewriteCond %{REQUEST_FILENAME} -d [OR] + RewriteCond %{REQUEST_FILENAME} gallery\_remote2\.php + RewriteCond %{REQUEST_URI} !/main\.php$ + RewriteRule . - [L] + + RewriteCond %{QUERY_STRING} g2_view=core.DownloadItem + RewriteCond %{QUERY_STRING} g2_itemId=([0-9]+) + RewriteCond %{HTTP:Referer} !^[a-zA-Z0-9\+\.\-]+://%{SERVER_NAME}/ [NC] + RewriteCond %{HTTP:Referer} !^$ + RewriteRule . /main.php?g2_view=watermark.DownloadItem&g2_itemId=%1 [L] + RewriteCond %{THE_REQUEST} /sitemap(\?.|\ .) + RewriteCond %{REQUEST_URI} !/main\.php$ + RewriteRule . /main.php?g2_view=sitemap.Sitemap [QSA,L] + RewriteCond %{THE_REQUEST} /mapa/(G{1}[0-9]+)*(\?.|\ .) + RewriteCond %{REQUEST_URI} !/main\.php$ + RewriteRule . /main.php?g2_view=mapv3.ShowMap&g2_Group=%1 [QSA,L] + RewriteCond %{THE_REQUEST} /fotografia/([^?]+)(\?.|\ .) + RewriteCond %{REQUEST_URI} !/main\.php$ + RewriteRule . /main.php?g2_path=%1 [QSA,L] + RewriteCond %{THE_REQUEST} /paraules/([^?/]+)(\?.|\ .) + RewriteCond %{REQUEST_URI} !/main\.php$ + RewriteRule . /main.php?g2_view=keyalbum.KeywordAlbum&g2_keyword=%1 [QSA,L] + + +# END Url Rewrite section + + + ExpiresActive On + ExpiresDefault A30600 + + + + Header append Cache-Control "public" + + + ExpiresDefault "access plus 7 day" + + diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index b380bfe2..99f7c89a 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -145,6 +145,21 @@ for `ca_ES` locale. Add a line like it using your locale: && sed -i -e 's/# ca_ES ISO-8859-1/ca_ES ISO-8859-1/' /etc/locale.gen \ ``` +## Copy volumes + +You can create a backup copy of the `mysqldata` and `gallery2-data` volumes to start afresh. + +There is a `copy-volumes.sh` script that allows you to create a copy of the volume. + +In case you would like to reset the state to the one in the vanilla volumes, you just need to copy data from the +vanilla volumes to the new volumes. + +You will need to add the volume as an external volume in docker-compose config file: +```yaml + gallery2-php8_mysqldata-fresh: + external: true +``` + # Release management Tools for release management are on folder `lib/tools/release`. diff --git a/docker-compose.yaml b/docker-compose.yaml index 53bcee0d..39dcf354 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -19,7 +19,7 @@ services: PHP_IDE_CONFIG: "serverName=docker" volumes: - .:/app - - ./gallery2-data:/gallery2-data + - gallery2-data:/gallery2-data - ./docker/php.ini:/usr/local/etc/php/conf.d/custom.ini - ./docker/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini links: @@ -32,6 +32,10 @@ services: MYSQL_PASSWORD: 'secret' MYSQL_DATABASE: 'gallery2' volumes: - - ./mysqldata:/var/lib/mysql + - mysqldata:/var/lib/mysql ports: - "13306:3306" + +volumes: + mysqldata: { } + gallery2-data: { } diff --git a/docker/copy-volumes.sh b/docker/copy-volumes.sh new file mode 100644 index 00000000..b90c77d3 --- /dev/null +++ b/docker/copy-volumes.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -u +set -a + +SOURCE_VOLUME=$1 +TARGET_VOLUME=$2 + +docker container run --rm -it -v $SOURCE_VOLUME:/from -v $TARGET_VOLUME:/to alpine ash -c "cd /from ; cp -av . /to" diff --git a/ga.js.sample b/ga.js.sample new file mode 100644 index 00000000..1d268405 --- /dev/null +++ b/ga.js.sample @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/install/steps/AuthenticateStep.class b/install/steps/AuthenticateStep.class index b77ca44c..efe13402 100644 --- a/install/steps/AuthenticateStep.class +++ b/install/steps/AuthenticateStep.class @@ -39,7 +39,7 @@ class AuthenticateStep extends InstallStep { function processRequest() { if (!empty($_GET['downloadLogin'])) { - GallerySetupUtilities::generateTextFileForDownload('login.txt', $this->_uniqueKey); + GallerySetupUtilities::generateTextFileForDownload('login.txt', $this::_uniqueKey); return false; } diff --git a/install/steps/CreateConfigFileStep.class b/install/steps/CreateConfigFileStep.class index d7a28404..3317a557 100644 --- a/install/steps/CreateConfigFileStep.class +++ b/install/steps/CreateConfigFileStep.class @@ -36,7 +36,7 @@ class CreateConfigFileStep extends InstallStep { function processRequest() { if (!empty($_GET['downloadConfig'])) { GallerySetupUtilities::generateTextFileForDownload( - 'config.php', $this->_getConfigContents()); + 'config.php', $this::_getConfigContents()); return false; } @@ -132,7 +132,7 @@ class CreateConfigFileStep extends InstallStep { $this->_firstTime = false; } - function _getConfigContents() { + static function _getConfigContents() { global $galleryStub; $configDir = $_SESSION['configPath']; $baseDir = dirname(dirname(dirname(__FILE__))); diff --git a/install/steps/DatabaseSetupStep.class b/install/steps/DatabaseSetupStep.class index 6899568a..7ab53596 100644 --- a/install/steps/DatabaseSetupStep.class +++ b/install/steps/DatabaseSetupStep.class @@ -71,9 +71,9 @@ class DatabaseSetupStep extends InstallStep { $this->_config['type'] = 'mysqli'; $mysqltType = 'mysqli'; } - if ($this->_config['type'] == 'pdo_sqlite') { - $this->_config['database'] = - 'sqlite:' . $galleryStub->getConfig('data.gallery.base') . 'gallery2.db'; + if ($this->_config['type'] == 'pdo_sqlite') { + $this->_config['database'] = + 'sqlite:' . $galleryStub->getConfig('data.gallery.base') . 'gallery2.db'; } switch ($this->_config['type']) { case 'mysql': @@ -154,7 +154,8 @@ class DatabaseSetupStep extends InstallStep { require_once(dirname(__FILE__) . '/../../lib/adodb/adodb.inc.php'); $this->_captureStart(); - $this->_db =& ADONewConnection($this->_config['type']); + $ADOConnection = ADONewConnection($this->_config['type']); + $this->_db =& $ADOConnection; $this->_captureEnd(); if (empty($this->_db)) { @@ -162,7 +163,7 @@ class DatabaseSetupStep extends InstallStep { _('Unable to create a database connection of type %s'), $this->_config['type']); } - + if (empty($templateData['errors'])) { $this->_captureStart(); $this->_db->debug = true; @@ -616,7 +617,7 @@ class DatabaseSetupStep extends InstallStep { $GLOBALS['gallery']->setConfig('plugins.dirname', $galleryStub->getConfig('plugins.dirname')); $GLOBALS['gallery']->setConfig('data.gallery.version', $configPath . 'versions.dat'); - $GLOBALS['gallery']->setConfig('data.gallery.locale', $configPath . 'locale' + $GLOBALS['gallery']->setConfig('data.gallery.locale', $configPath . 'locale' . DIRECTORY_SEPARATOR); } global $gallery; diff --git a/lib/support/GallerySetupUtilities.class b/lib/support/GallerySetupUtilities.class index 4f8cc7e6..22ecdf2a 100644 --- a/lib/support/GallerySetupUtilities.class +++ b/lib/support/GallerySetupUtilities.class @@ -290,13 +290,14 @@ class GallerySetupUtilities { * @param string $name the file's name * @param string $contents the file's contents */ - function generateTextFileForDownload($name, $contents) { - header('Content-Type: text/plain'); - header('Content-Length: ' . strlen($contents)); - header("Content-Description: Download $name to your computer."); - header("Content-Disposition: attachment; filename=$name"); - print $contents; - } + static function generateTextFileForDownload($name, $contents) + { + header('Content-Type: text/plain'); + header('Content-Length: ' . strlen($contents)); + header("Content-Description: Download $name to your computer."); + header("Content-Disposition: attachment; filename=$name"); + print $contents; + } /** * Cleanly start up our session. diff --git a/lib/tools/bin/.htaccess b/lib/tools/bin/.htaccess new file mode 100644 index 00000000..cb24fd7f --- /dev/null +++ b/lib/tools/bin/.htaccess @@ -0,0 +1,2 @@ +Order allow,deny +Deny from all diff --git a/lib/tools/bin/GNUmakefile.GalleryStorage b/lib/tools/bin/GNUmakefile.GalleryStorage new file mode 100644 index 00000000..ca1190e8 --- /dev/null +++ b/lib/tools/bin/GNUmakefile.GalleryStorage @@ -0,0 +1,42 @@ +CLASSDIR ?= .. +TOOLDIR ?= ../../../../lib/tools +TMPDIR ?= tmp +RXP ?= $(shell which rxp 2>/dev/null) +XMLLINT ?= $(shell which xmllint 2>/dev/null) +XMLFILES = $(wildcard xml-src/*.xml) +PHP ?= php + +all: validate tmp dbxml sql + +tmp: + @if [ ! -d tmp ]; then mkdir tmp; fi + @if [ ! -d tmp/classxml ]; then mkdir tmp/classxml; fi + @if [ ! -d tmp/dbxml ]; then mkdir tmp/dbxml; fi + +dbxml: + @if [ "x" != "$(wildcard $(CLASSDIR)/*.class)x" ]; then \ + perl $(TOOLDIR)/bin/extractClassXml.pl --dtd=$(TOOLDIR)/dtd/GalleryClass2.1.dtd --quiet --out-dir=tmp/classxml $(CLASSDIR)/*.class; \ + else \ + echo "No .class files to parse!"; \ + fi + @if [ "x" != "$(XMLFILES)x" ]; then cp xml-src/*.xml tmp/dbxml; fi + $(PHP) -q -C -f $(TOOLDIR)/bin/generate-dbxml.php + +sql: + $(PHP) -q -C -f $(TOOLDIR)/bin/generate-sql.php + +clean: + rm -rf tmp + +validate: + @if [ "x" != "$(XMLFILES)x" ]; then \ + if [ -x "$(XMLLINT)" ]; then \ + xmllint --valid --noout $(XMLFILES); \ + elif [ -x "$(RXP)" ]; then \ + for xmlfile in $(XMLFILES); do \ + rxp -sV $$xmlfile; \ + done \ + else \ + echo "No available XML validators (need xmllint or rxp)"; \ + fi \ + fi diff --git a/lib/tools/bin/GNUmakefile.classes b/lib/tools/bin/GNUmakefile.classes new file mode 100644 index 00000000..7c59d12a --- /dev/null +++ b/lib/tools/bin/GNUmakefile.classes @@ -0,0 +1,26 @@ +RXP ?= $(shell which rxp 2>/dev/null) +XMLLINT ?= $(shell which xmllint 2>/dev/null) +PHP ?= php +CLASSES = $(shell ls *.class 2>/dev/null) + +all: Entities.inc + if [ -f Maps.xml ]; then $(MAKE) Maps.inc; fi + if [ -f GalleryStorage/GNUmakefile ]; then cd GalleryStorage && $(MAKE); fi + +%: + if [ -f GalleryStorage/GNUmakefile ]; then cd GalleryStorage && $(MAKE) $@; fi + +Maps.inc: Maps.xml ../../../lib/tools/bin/maps.tpl + @if [ -x "$(XMLLINT)" ]; then \ + xmllint --valid --noout Maps.xml; \ + elif [ -x "$(RXP)" ]; then \ + for xmlfile in $(XMLFILES); do \ + rxp -sV Maps.xml; \ + done \ + else \ + echo "No available XML validators (need xmllint or rxp)"; \ + fi + $(PHP) -q -C ../../../lib/tools/bin/generate-maps.php + +Entities.inc: $(CLASSES) ../../../lib/tools/bin/entities.tpl + $(PHP) -q -C ../../../lib/tools/bin/generate-entities.php diff --git a/lib/tools/bin/XmlParser.inc b/lib/tools/bin/XmlParser.inc new file mode 100644 index 00000000..4e05da66 --- /dev/null +++ b/lib/tools/bin/XmlParser.inc @@ -0,0 +1,84 @@ +_xmlObject = xml_parser_create(); + xml_set_object($this->_xmlObject, $this); + xml_set_character_data_handler($this->_xmlObject, 'dataHandler'); + xml_set_element_handler($this->_xmlObject, 'startHandler', 'endHandler'); + } + + public function parse($path) { + $data = file_get_contents($path); + + if ($data === false) { + die('Cannot open XML data file: ' . $path); + } + + if (!xml_parse($this->_xmlObject, $data, 1)) { + die( + sprintf( + 'XML error: %s at line %d', + xml_error_string(xml_get_error_code($this->_xmlObject)), + xml_get_current_line_number($this->_xmlObject) + ) + ); + xml_parser_free($this->_xmlObject); + } + + return $this->_output; + } + + public function startHandler($parser, $name, $attribs) { + $content = array( + 'name' => $name, + ); + + if (!empty($attribs)) { + $content['attrs'] = $attribs; + } + array_push($this->_output, $content); + } + + public function dataHandler($parser, $data) { + if (isset($data)) { + $outputIndex = count($this->_output) - 1; + $this->_output[$outputIndex]['content'] = $data; + } + } + + public function endHandler($parser, $name) { + if (count($this->_output) > 1) { + $data = array_pop($this->_output); + $outputIndex = count($this->_output) - 1; + $this->_output[$outputIndex]['child'][] = $data; + } + } + + public function cleanUp() { + xml_parser_free($this->_xmlObject); + } +} diff --git a/lib/tools/bin/dbxml.tpl b/lib/tools/bin/dbxml.tpl new file mode 100644 index 00000000..bf9a47ef --- /dev/null +++ b/lib/tools/bin/dbxml.tpl @@ -0,0 +1,54 @@ + + + + + {$schema.name} + + {$schema.major} + {$schema.minor} + +{if $requiresId} + + id + INTEGER + MEDIUM + + +{/if} +{foreach from=$members item=member} + + {$member.name} + {$member.type} + {$member.size} +{if isset($member.required) || isset($member.primary)} +{if !empty($member.required.empty)} + +{else} + +{/if} +{/if} +{if isset($member.default)} + {$member.default} +{/if} + +{/foreach} +{foreach from=$keys item=key} +{if !empty($key.primary)} + +{else} + +{/if} +{foreach from=$key.columns item=column} + {$column} +{/foreach} + +{/foreach} +{foreach from=$indexes item=index} + +{foreach from=$index.columns item=column} + {$column} +{/foreach} + +{/foreach} +
diff --git a/lib/tools/bin/entities.tpl b/lib/tools/bin/entities.tpl new file mode 100644 index 00000000..6c7efb9b --- /dev/null +++ b/lib/tools/bin/entities.tpl @@ -0,0 +1,15 @@ + array( +{foreach name=outer from=$entity.members key=memberName item=memberInfo} + '{$memberName}' => array({foreach name=inner from=$memberInfo key=key item=value}'{$key}' => {$value}{if !$smarty.foreach.inner.last}, {/if}{/foreach}){if !$smarty.foreach.outer.last},{/if} + +{/foreach}), + 'parent' => {if $entity.parent=='GalleryPersistent'}null{else}'{$entity.parent}'{/if}, + 'module' => '{$entity.module}', + 'linked' => array({foreach name=linked from=$entity.linked item=value}'{$value}'{if !$smarty.foreach.linked.last},{/if}{/foreach})); +{/foreach} +?> + diff --git a/lib/tools/bin/extractClassXml.pl b/lib/tools/bin/extractClassXml.pl new file mode 100755 index 00000000..6c0ab012 --- /dev/null +++ b/lib/tools/bin/extractClassXml.pl @@ -0,0 +1,62 @@ +#!/usr/local/bin/perl +# +use strict; +use File::Basename; +use Getopt::Long; + +my $DTD; +my $OUTFILE; +my $STUB_OK = 0; +my $QUIET = 0; +my $OUT_DIR; + +GetOptions("dtd:s" => \$DTD, + "out:s" => \$OUTFILE, + "out-dir:s" => \$OUT_DIR, + "stub-ok+" => \$STUB_OK, + "quiet!" => \$QUIET); + +foreach my $file (@ARGV) { + my $tagCount = 0; + my $base = basename($file); + $base =~ s/\..*?$//; + my $xml = $OUTFILE || "$OUT_DIR/$base.xml"; + my $schemaName = undef; + + open(IFD, "<$file") || die; + open(OFD, ">$xml") || die; + print OFD "\n"; + print OFD "\n"; + while () { + if (s/.*\@g2\s*//) { + $tagCount++; + print OFD $_; + + # NOTE! Keep this in sync with the similar block in generate-entities.php + # and generate-maps.php + if (m|(.*)|) { + ($schemaName = $1) =~ s/^Gallery//; + # Shorten some table names to fit Oracle's 30 char name limit.. + $schemaName =~ s/Preferences/Prefs/; + $schemaName =~ s/Toolkit/Tk/; + $schemaName =~ s/TkOperation/TkOperatn/; + } + + if (m||) { + print OFD "$schemaName\n"; + } + } + } + close(IFD); + print OFD "\n"; + close(OFD); + + # It's gotta have more than the class-name, schema-version tags. + # + if ($tagCount == 0 || ($tagCount <= 2 && !$STUB_OK)) { + print STDERR "No tags detected\n" unless ($QUIET); + unlink($xml); + } +} + +exit 0; diff --git a/lib/tools/bin/generate-dbxml.php b/lib/tools/bin/generate-dbxml.php new file mode 100644 index 00000000..e0d99163 --- /dev/null +++ b/lib/tools/bin/generate-dbxml.php @@ -0,0 +1,376 @@ +compile_dir = $tmpdir; +$smarty->error_reporting = error_reporting(); +$smarty->debugging = true; +$smarty->use_sub_dirs = false; +$smarty->template_dir = __DIR__; + +function generateEntityDbXml() { + global $smarty; + + $entityXmlFiles = glob('tmp/classxml/*.xml'); + + if (empty($entityXmlFiles)) { + return; + } + + foreach ($entityXmlFiles as $xmlFile) { + $p = new XmlParser(); + $root = $p->parse($xmlFile); + $base = basename($xmlFile); + $base = preg_replace('/\.[^\.]*$/', '', $base); + $tmpFile = "tmp/dbxml/$base.xml"; + $origFile = "$base.xml"; + + $membersBase = $root[0]['child']; + $schema = array( + 'name' => $root[0]['child'][2]['child'][0]['content'], + 'major' => $root[0]['child'][2]['child'][1]['content'], + 'minor' => (!empty($root[0]['child'][2]['child'][2]['content']) ? $root[0]['child'][2]['child'][2]['content'] : 0), + ); + + $members = array(); + $keys = array(); + $indexes = array(); + $requiresId = false; + + foreach ($membersBase as $child) { + switch ($child['name']) { + case 'MEMBER': + $member = array( + 'name' => $child['child'][0]['content'], + 'type' => $child['child'][1]['content'], + 'ucName' => ucfirst($child['child'][0]['content']), + 'lcType' => strtolower($child['child'][1]['content']), + ); + + for ($i = 2; $i < count($child['child']); $i++) { + switch ($child['child'][$i]['name']) { + case 'MEMBER-SIZE': + $member['size'] = $child['child'][$i]['content']; + + break; + + case 'INDEXED': + $indexes[] = array( + 'columns' => array($member['name']), + ); + $member[strtolower($child['child'][$i]['name'])] = 1; + + break; + + case 'UNIQUE': + $keys[] = array( + 'columns' => array($member['name']), + ); + $member[strtolower($child['child'][$i]['name'])] = 1; + + break; + + case 'PRIMARY': + $keys[] = array( + 'columns' => array($member['name']), + 'primary' => 1, + ); + $member['primary'] = 1; + + break; + + case 'ID': + case 'LINKED': + $member[strtolower($child['child'][$i]['name'])] = 1; + + break; + + case 'REQUIRED': + $member['required'] = array(); + + if (isset($child['child'][$i]['attrs']['EMPTY'])) { + $member['required']['empty'] = $child['child'][$i]['attrs']['EMPTY']; + } else { + $member['required']['empty'] = 'disallowed'; + } + + break; + + case 'DEFAULT': + $member['default'] = $child['child'][$i]['content']; + + break; + + case 'MEMBER-EXTERNAL-ACCESS': + // Not relevant for storage layer + break; + + default: + print 'Unknown member type: ' . $child['child'][$i]['name'] . '\n'; + } + } + + if (empty($member['size'])) { + $member['size'] = 'MEDIUM'; + } + + $members[] = $member; + + break; + + case 'KEY': + $key = array(); + + foreach ($child['child'] as $column) { + $key['columns'][] = $column['content']; + } + $key['primary'] = isset($child['attrs']['PRIMARY']) && $child['attrs']['PRIMARY'] == 'true'; + $keys[] = $key; + + break; + + case 'INDEX': + $index = array(); + + foreach ($child['child'] as $column) { + $index['columns'][] = $column['content']; + } + $index['primary'] = isset($child['attrs']['PRIMARY']) && $child['attrs']['PRIMARY'] == 'true'; + + $indexes[] = $index; + + break; + + case 'REQUIRES-ID': + $requiresId = true; + $keys[] = array( + 'columns' => array('id'), + 'primary' => 1, + ); + + break; + } + } + + $smarty->assign('root', $root); + $smarty->assign('schema', $schema); + $smarty->assign('members', $members); + $smarty->assign('keys', $keys); + $smarty->assign('indexes', $indexes); + $smarty->assign('requiresId', $requiresId); + $smarty->assign('isMap', false); + $new = $smarty->fetch('dbxml.tpl'); + + $fd = fopen($tmpFile, 'w'); + fwrite($fd, $new); + fclose($fd); + } +} + +function generateMapDbXml() { + global $smarty; + + $xmlFile = '../Maps.xml'; + + if (!file_exists($xmlFile)) { + return; + } + + $p = new XmlParser(); + $root = $p->parse($xmlFile); + $base = basename($xmlFile); + $base = preg_replace('/\.[^\.]*$/', '', $base); + $origFile = "$base.xml"; + + foreach ($root[0]['child'] as $map) { + $origMapName = $map['child'][0]['content']; + + /* + * NOTE! Keep this in sync with the similar block in extractClassXml.pl + * and generate-entities.php + */ + $mapName = $origMapName; + $mapName = preg_replace('/^Gallery/', '', $mapName); + // Shorten some table names to fit Oracle's 30 char name limit.. + $mapName = str_replace('Preferences', 'Prefs', $mapName); + $mapName = str_replace('Toolkit', 'Tk', $mapName); + $mapName = str_replace('TkOperation', 'TkOperatn', $mapName); + + $schema = array( + 'name' => $mapName, + 'major' => $map['child'][1]['child'][0]['content'], + 'minor' => $map['child'][1]['child'][1]['content'], + ); + $tmpFile = "tmp/dbxml/$origMapName.xml"; + + $members = array(); + $keys = array(); + $indexes = array(); + $requiresId = false; + + for ($j = 2; $j < count($map['child']); $j++) { + $child = $map['child'][$j]; + + switch ($child['name']) { + case 'MEMBER': + $member = array( + 'name' => $child['child'][0]['content'], + 'type' => $child['child'][1]['content'], + 'ucName' => ucfirst($child['child'][0]['content']), + 'lcType' => strtolower($child['child'][1]['content']), + ); + + for ($i = 2; $i < count($child['child']); $i++) { + switch ($child['child'][$i]['name']) { + case 'MEMBER-SIZE': + $member['size'] = $child['child'][$i]['content']; + + break; + + case 'INDEXED': + $indexes[] = array( + 'columns' => array($member['name']), + ); + $member[strtolower($child['child'][$i]['name'])] = 1; + + break; + + case 'UNIQUE': + $keys[] = array( + 'columns' => array($member['name']), + ); + $member[strtolower($child['child'][$i]['name'])] = 1; + + break; + + case 'PRIMARY': + $keys[] = array( + 'columns' => array($member['name']), + 'primary' => 1, + ); + $member['primary'] = 1; + + break; + + case 'REQUIRED': + $member['required'] = array(); + + if (isset($child['child'][$i]['attrs']['EMPTY'])) { + $member['required']['empty'] = $child['child'][$i]['attrs']['EMPTY']; + } else { + $member['required']['empty'] = 'disallowed'; + } + + break; + + case 'DEFAULT': + $member['default'] = $child['child'][$i]['content']; + + break; + + case 'MEMBER-EXTERNAL-ACCESS': + // Not relevant for storage layer + break; + + default: + print 'Unknown member type: ' . $child['child'][$i]['name'] . '\n'; + } + } + + if (empty($member['size'])) { + $member['size'] = 'MEDIUM'; + } + + $members[] = $member; + + break; + + case 'KEY': + $key = array(); + + foreach ($child['child'] as $column) { + $key['columns'][] = $column['content']; + } + $key['primary'] = isset($child['attrs']['PRIMARY']) && $child['attrs']['PRIMARY'] == 'true'; + $keys[] = $key; + + break; + + case 'INDEX': + $index = array(); + + foreach ($child['child'] as $column) { + $index['columns'][] = $column['content']; + } + $index['primary'] = isset($child['attrs']['PRIMARY']) && $child['attrs']['PRIMARY'] == 'true'; + + $indexes[] = $index; + + break; + } + + $smarty->assign('root', $root); + $smarty->assign('schema', $schema); + $smarty->assign('members', $members); + $smarty->assign('keys', $keys); + $smarty->assign('indexes', $indexes); + $smarty->assign('requiresId', $requiresId); + $smarty->assign('isMap', true); + $new = $smarty->fetch('dbxml.tpl'); + + $fd = fopen($tmpFile, 'w'); + fwrite($fd, $new); + fclose($fd); + } + } +} + +generateEntityDbXml(); +generateMapDbXml(); + +// Clean up the cheap and easy way +if (file_exists($tmpdir)) { + system("rm -rf $tmpdir"); +} diff --git a/lib/tools/bin/generate-entities.php b/lib/tools/bin/generate-entities.php new file mode 100644 index 00000000..96c7934b --- /dev/null +++ b/lib/tools/bin/generate-entities.php @@ -0,0 +1,252 @@ +compile_dir = $tmpdir; +$smarty->error_reporting = error_reporting(); +$smarty->debugging = true; +$smarty->use_sub_dirs = false; +$smarty->template_dir = __DIR__; + +// Grab all G2 XML from entity class files + +$xml = '\n"; +$xml .= "\n"; + +if (!$dh = opendir('.')) { + echo "Unable to opendir(.)\n"; + cleanExit(1); +} + +$files = array(); + +while (($file = readdir($dh)) !== false) { + if (preg_match('/\.class$/', $file)) { + $files[] = $file; + } +} +closedir($dh); +sort($files); +$classXml = ''; + +foreach ($files as $file) { + $snippet = getXml($file); + + if ($snippet) { + $classXml .= "\n" . join("\n", $snippet) . "\n\n"; + } +} + +if (empty($classXml)) { + // Nothing to do + cleanExit(0); +} + +$xml .= $classXml; +$xml .= "\n"; + +$entitiesXml = "$tmpdir/Entities.xml"; + +if (!$fp = fopen($entitiesXml, 'wb')) { + echo "Unable to write to $entitiesXml\n"; + cleanExit(1); +} +fwrite($fp, $xml); +fclose($fp); + +if (system("xmllint --valid --noout $entitiesXml", $retval)) { + echo "System error: $retval\n"; + cleanExit(); +} + +$p = new XmlParser(); +$root = $p->parse($entitiesXml); + +$entities = array(); + +foreach ($root[0]['child'] as $entity) { + $entityName = $entity['child'][0]['content']; + $parentEntityName = $entity['child'][1]['content']; + + $j = 3; + + if ($entity['child'][$j]['name'] == 'REQUIRES-ID') { + $j++; + } + + $entities[$entityName]['members'] = array(); + $entities[$entityName]['linked'] = array(); + + for (; $j < count($entity['child']); $j++) { + $member = $entity['child'][$j]; + $name = $member['child'][0]['content']; + + $entities[$entityName]['members'][$name]['type'] = 'STORAGE_TYPE_' . $member['child'][1]['content']; + $entities[$entityName]['members'][$name]['type'] = 'STORAGE_TYPE_' . $member['child'][1]['content']; + + for ($k = 2; $k < count($member['child']); $k++) { + if (!empty($member['child'][$k]['name'])) { + switch ($member['child'][$k]['name']) { + case 'MEMBER-SIZE': + $entities[$entityName]['members'][$name]['size'] = $size = 'STORAGE_SIZE_' . $member['child'][$k]['content']; + + break; + + case 'ID': + $entities[$entityName]['members'][$name]['type'] .= '| STORAGE_TYPE_ID'; + + break; + + case 'LINKED': + $entities[$entityName]['linked'][] = $name; + + break; + + case 'REQUIRED': + case 'PRIMARY': + $elem = $member['child'][$k]; + + if ($elem['name'] != 'REQUIRED' || empty($elem['attrs']['EMPTY']) + || $elem['attrs']['EMPTY'] != 'allowed' + ) { + $entities[$entityName]['members'][$name]['notNull'] = true; + } else { + $entities[$entityName]['members'][$name]['notNullEmptyAllowed'] = true; + } + + break; + + case 'MEMBER-EXTERNAL-ACCESS': + switch (trim($member['child'][$k]['content'])) { + case 'READ': + $entities[$entityName]['members'][$name]['external-access'] = 'EXTERNAL_ACCESS_READ'; + + break; + + case 'WRITE': + $entities[$entityName]['members'][$name]['external-access'] = 'EXTERNAL_ACCESS_WRITE'; + + break; + + case 'FULL': + $entities[$entityName]['members'][$name]['external-access'] = 'EXTERNAL_ACCESS_FULL'; + + break; + + default: + printf( + 'Unknown value for member-external-access "%s"\n', + $member['child'][$k]['content'] + ); + } + + break; + } + } + } + } + + $entities[$entityName]['parent'] = $parentEntityName; + $entities[$entityName]['module'] = basename(dirname(realpath('.'))); +} + +$smarty->assign('entities', $entities); +$new = $smarty->fetch('entities.tpl'); + +// Windows leaves a CR at the end of the file +$new = rtrim($new, "\r"); + +$fd = fopen('Entities.inc', 'w'); +fwrite($fd, $new); +fclose($fd); + +// Done +cleanExit(0); + +function cleanExit($status = 0) { + // Clean up the cheap and easy way + global $tmpdir; + + if (file_exists($tmpdir)) { + system("rm -rf $tmpdir"); + } + + exit($status); +} + +function getXml($filename) { + $results = array(); + + if ($fp = fopen($filename, 'rb')) { + while (!feof($fp)) { + $line = fgets($fp, 4096); + + if (preg_match('/@g2(.*)/', $line, $matches)) { + $results[] = $line = $matches[1]; + + /* + * NOTE! Keep this in sync with the similar block in extractClassXml.pl + * and generate-dbxml.php + */ + if (preg_match('{(.*)}', $line, $matches)) { + $schemaName = $matches[1]; + $schemaName = preg_replace('/^Gallery/', '', $schemaName); + // Shorten some table names to fit Oracle's 30 char name limit.. + $schemaName = preg_replace('/Preferences/', 'Prefs', $schemaName); + $schemaName = preg_replace('/Toolkit/', 'Tk', $schemaName); + $schemaName = preg_replace('/TkOperation/', 'TkOperatn', $schemaName); + } + + if (preg_match('{}', $line)) { + $results[] = " $schemaName"; + } + } + } + fclose($fp); + } + + return $results; +} diff --git a/lib/tools/bin/generate-maps.php b/lib/tools/bin/generate-maps.php new file mode 100644 index 00000000..f2db9226 --- /dev/null +++ b/lib/tools/bin/generate-maps.php @@ -0,0 +1,131 @@ +compile_dir = $tmpdir; +$smarty->error_reporting = error_reporting(); +$smarty->debugging = true; +$smarty->use_sub_dirs = false; +$smarty->template_dir = __DIR__; + +$xmlFile = 'Maps.xml'; + +if (!file_exists($xmlFile)) { + echo "Missing Maps.xml, can't continue.\n"; + cleanExit(1); +} + +$p = new XmlParser(); +$root = $p->parse($xmlFile); + +$maps = array(); + +foreach ($root[0]['child'] as $map) { + $mapName = $map['child'][0]['content']; + + for ($j = 2; $j < count($map['child']); $j++) { + $child = $map['child'][$j]; + + if ($child['name'] == 'MEMBER') { + $member = array( + 'name' => $child['child'][0]['content'], + 'type' => 'STORAGE_TYPE_' . $child['child'][1]['content'], + ); + + if (!empty($child['child'][2]['name']) + && $child['child'][2]['name'] == 'MEMBER-SIZE' + ) { + $member['size'] = 'STORAGE_SIZE_' . $child['child'][2]['content']; + } else { + $member['size'] = 'STORAGE_SIZE_MEDIUM'; + } + + for ($k = 2; $k < count($child['child']); $k++) { + if (!empty($child['child'][$k]['name'])) { + $elem = $child['child'][$k]; + + if ($elem['name'] == 'PRIMARY' || $elem['name'] == 'REQUIRED') { + if ($elem['name'] != 'REQUIRED' || empty($elem['attrs']['EMPTY']) + || $elem['attrs']['EMPTY'] != 'allowed' + ) { + $member['notNull'] = true; + } else { + $member['notNullEmptyAllowed'] = true; + } + + break; + } + } + } + + $maps[$mapName][] = $member; + } + } +} + +$smarty->assign('maps', $maps); +$smarty->assign('mapName', $mapName); +$new = $smarty->fetch('maps.tpl'); + +// Windows leaves a CR at the end of the file +$new = rtrim($new, "\r"); + +$fd = fopen('Maps.inc', 'w'); +fwrite($fd, $new); +fclose($fd); + +// Done +cleanExit(0); + +function cleanExit($status = 0) { + // Clean up the cheap and easy way + global $tmpdir; + + if (file_exists($tmpdir)) { + system("rm -rf $tmpdir"); + } + + exit($status); +} diff --git a/lib/tools/bin/generate-sql.php b/lib/tools/bin/generate-sql.php new file mode 100755 index 00000000..ffc1f336 --- /dev/null +++ b/lib/tools/bin/generate-sql.php @@ -0,0 +1,1986 @@ +parse($xmlFile); + + $generatorClass = "${db}Generator"; + $generator = new $generatorClass(); + + $base = basename($xmlFile); + $base = preg_replace('/\.[^\.]*$/', '', $base); + $output .= '# ' . $base . "\n"; + $root[0]['base'] = $base; + $output .= $generator->createSql($root[0], 0, 0, null); + } +} + +$fd = fopen('schema.tpl', 'w'); +fwrite($fd, $output); +fclose($fd); + +class BaseGenerator { + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'SCHEMA': + $output .= "INSERT INTO DB_TABLE_PREFIXSchema (\n"; + $output .= " DB_COLUMN_PREFIXname,\n"; + $output .= " DB_COLUMN_PREFIXmajor,\n"; + $output .= " DB_COLUMN_PREFIXminor\n"; + $output .= ') VALUES('; + $output .= "'" . $parent['child'][0]['content'] . "', " . $child[0]['content'] . ', ' . + $child[1]['content']; + $output .= ");\n\n"; + + break; + + case 'COLUMN': + // column-name, column-type, column-size, not-null? + $output .= ' DB_COLUMN_PREFIX' . $child[0]['content']; + $output .= ' ' . $this->columnDefinition($child); + + break; + + default: + $output .= "1. UNIMPLEMLENTED: $node[name]"; + + break; + } + + return $output; + } + + public function getIndexCrc($columns) { + $buf = ''; + + for ($i = 0; $i < count($columns); $i++) { + $buf .= $columns[$i]['content']; + } + + /* + * crc32 returns different results on 32-bit vs. 64-bit systems. e.g. crc32('groupId') + * returns -310277968 for 32-bit systems and 3984689328 on 64-bit systems. We don't + * completely understand the issue, but adding 2^32 for negative crc32 values + * (32-bit overflows?!) seems to do the trick. And we eschew the 64-bit unsafe modulo + * operation by using substr instead of % 100000. + * Note: We also want strictly positive values since we use the value in SQL index key + * names. + */ + $crc = crc32($buf); + + if ($crc > 0) { + return $crc % 100000; + } + + return (int)substr(crc32($buf) + 2 ** 32, -5); + } + + public function getNotNullElement($child) { + for ($i = 0; $i < count($child); $i++) { + if ($child[$i]['name'] == 'NOT-NULL') { + return $child[$i]; + } + } + + return null; + } + + public function getDefaultElement($child) { + for ($i = 0; $i < count($child); $i++) { + if ($child[$i]['name'] == 'DEFAULT') { + return $child[$i]['content']; + } + } + + return null; + } + + public function setColumnDefinitionMap($map) { + $this->_columnDefinitionMap = $map; + } + + public function columnDefinition($child, $includeNotNull = true, $includeDefault = true) { + $output = ''; + $key = $child[1]['content'] . '-' . + (!empty($child[2]['content']) ? $child[2]['content'] : ''); + + if (isset($this->_columnDefinitionMap[$key])) { + $output .= $this->_columnDefinitionMap[$key]; + } else { + $output .= "2. UNIMPLEMLENTED: $key"; + } + + if ($includeDefault) { + $defaultValue = $this->getDefaultElement($child); + + if (isset($defaultValue)) { + $output .= " DEFAULT '$defaultValue'"; + } + } + + if ($includeNotNull) { + if ($this->getNotNullElement($child)) { + $output .= ' NOT NULL'; + } + } + + return $output; + } + + public function generateSchemaUpdate($child) { + $output = "UPDATE DB_TABLE_PREFIXSchema\n"; + $output .= sprintf( + " SET DB_COLUMN_PREFIXmajor=%d, DB_COLUMN_PREFIXminor=%d\n", + $child[2]['child'][0]['content'], + $child[2]['child'][1]['content'] + ); + $output .= sprintf( + " WHERE DB_COLUMN_PREFIXname='%s' AND DB_COLUMN_PREFIXmajor=%d " . + "AND DB_COLUMN_PREFIXminor=%d;\n\n", + $child[0]['content'], + $child[1]['child'][0]['content'], + (!empty($child[1]['child'][1]['content']) ? $child[1]['child'][1]['content'] : 0) + ); + + return $output; + } + + public function isPrimaryKey($child) { + return $this->isIndex($child) && !empty($child['attrs']['PRIMARY']); + } + + public function isIndex($child) { + return $child['name'] == 'INDEX'; + } +} + +class MySqlGenerator extends BaseGenerator { + public function __construct() { + $this->setColumnDefinitionMap( + array( + 'INTEGER-' => 'int(11)', + 'INTEGER-MEDIUM' => 'int(11)', + 'INTEGER-LARGE' => 'int(11)', + 'BIT-LARGE' => 'int(11)', + 'BIT-MEDIUM' => 'int(11)', + 'STRING-SMALL' => 'varchar(32)', + 'STRING-MEDIUM' => 'varchar(128)', + 'STRING-LARGE' => 'varchar(255)', + 'TEXT-SMALL' => 'text', + 'TEXT-' => 'text', + 'TEXT-MEDIUM' => 'text', + 'TEXT-LARGE' => 'longtext', + 'BOOLEAN-' => 'int(1)', + 'BOOLEAN-MEDIUM' => 'int(1)', + 'TIMESTAMP-' => 'datetime', + ) + ); + } + + public function columnDefinition($child, $includeNotNull = true, $includeDefault = true) { + $output = parent::columnDefinition($child, $includeNotNull, false); + + // MySQL -> DEFAULT expression after NOT NULL + if ($includeDefault) { + $defaultValue = $this->getDefaultElement($child); + + if (isset($defaultValue)) { + $output .= " DEFAULT '$defaultValue'"; + } + } + + return $output; + } + + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'TABLE': + // table-name, schema, column+, (key | index) + $output .= 'CREATE TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "(\n"; + + for ($i = 2; $i < count($child); $i++) { + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + + if ($i < count($child) - 1) { + $output .= ','; + } + $output .= "\n"; + } + $output .= ") DB_TABLE_TYPE\n"; + // Character set, enclosed in comments that are ignored by MySQL < 4.1.0 + $output .= "/*!40100 DEFAULT CHARACTER SET utf8 */;\n\n"; + + // Schema info + $output .= $this->createSql($child[1], 0, 0, $node); + + break; + + case 'ALTER': + // column+ + for ($i = 0; $i < count($child); $i++) { + $output .= ' MODIFY COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($child[$i]['child']); + + if ($i < count($child) - 1) { + $output .= ",\n"; + } + } + + break; + + case 'CHANGE': + // table-name, schema-from, schema-to, (add, alter, remove)+ + if (count($child) > 3) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "\n"; + + for ($i = 3; $i < count($child); $i++) { + if ($i > 3) { + $output .= ",\n"; + } + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + } + $output .= ";\n\n"; + } + $output .= $this->generateSchemaUpdate($child); + + break; + + case 'ADD': + // (column, key, index)+ + for ($i = 0; $i < count($child); $i++) { + $c = $child[$i]; + + switch ($c['name']) { + case 'COLUMN': + // column-name + $output .= ' ADD COLUMN DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($c['child']); + + break; + + case 'KEY': + $output .= ' ADD' . $this->createSql($c, 0, 0, null); + + break; + + case 'INDEX': + // column-name + $output .= ' ADD INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $c['child']; + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($columns); + } + $output .= '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + + break; + + default: + $output .= "3. UNIMPLEMLENTED: ADD $c[name]\n"; + } + + if ($i < count($child) - 1) { + $output .= ",\n"; + } + } + + break; + + case 'REMOVE': + if (!isset($parent['name'])) { + $output .= 'DROP TABLE DB_TABLE_PREFIX' . $node['child'][0]['content'] . ";\n\n"; + + if ($node['child'][0]['content'] != 'Schema') { + $output .= "DELETE FROM DB_TABLE_PREFIXSchema WHERE DB_COLUMN_PREFIXname='" . + $node['child'][0]['content'] . "';\n\n"; + } + } elseif ($parent['name'] == 'CHANGE') { + // (column-name, key, index)+ + $i = 0; + + foreach ($child as $c) { + if ($i++ > 0) { + $output .= ",\n"; + } + + switch ($c['name']) { + case 'COLUMN-NAME': + $output .= ' DROP COLUMN DB_COLUMN_PREFIX' . $c['content']; + + break; + + case 'KEY': + if (!empty($child[0]['attrs']['PRIMARY'])) { + $output .= ' DROP PRIMARY KEY'; + } else { + /* + * For MySQL, our UNIQUE index names are the name of the first + * column that is part of the index (MySQL sets the name that way + * for unnamed indices (they only need to be unique in each table) + */ + $output .= ' DROP INDEX DB_COLUMN_PREFIX' . $c['child'][0]['content']; + } + + break; + + case 'INDEX': + // column-name + $output .= ' DROP INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + + if (isset($child[0]['attrs'][$nameKey])) { + $output .= $child[0]['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($c['child']); + } + + break; + + default: + $output .= "4. UNIMPLEMENTED: REMOVE $c[name]\n"; + } + } + } + + break; + + case 'KEY': + // column-name+ + if (!empty($node['attrs']['PRIMARY'])) { + $output .= ' PRIMARY KEY('; + } else { + /* + * In MySQL, it would be UNIQUE [INDEX] so INDEX is optional, since UNIQUE is + * often called a KEY and we use in our XML for UNIQUE, we just use UNIQUE + * without INDEX here. Don't add an index name, see our REMOVE code. + */ + $output .= ' UNIQUE ('; + } + + for ($i = 0; $i < count($child); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['content']; + + if ($i < count($child) - 1) { + $output .= ', '; + } + } + $output .= ')'; + + break; + + case 'INDEX': + // column-name+ + $crc = $this->getIndexCrc($child); + $output .= ' INDEX DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '_' . $crc . '('; + + for ($i = 0; $i < count($child); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['content']; + + if ($i < count($child) - 1) { + $output .= ', '; + } + } + $output .= ')'; + + break; + + default: + $output .= parent::createSql($node, $index, $lastPeerIndex, $parent); + } + + return $output; + } + + public function getDbType() { + return 'mysql'; + } +} + +class PostgresGenerator extends BaseGenerator { + public function __construct() { + $this->setColumnDefinitionMap( + array( + 'INTEGER-' => 'INTEGER', + 'INTEGER-MEDIUM' => 'INTEGER', + 'INTEGER-LARGE' => 'INTEGER', + 'BIT-LARGE' => 'BIT(32)', + 'BIT-MEDIUM' => 'BIT(32)', + 'STRING-SMALL' => 'VARCHAR(32)', + 'STRING-MEDIUM' => 'VARCHAR(128)', + 'STRING-LARGE' => 'VARCHAR(255)', + 'TEXT-SMALL' => 'text', + 'TEXT-' => 'text', + 'TEXT-MEDIUM' => 'text', + 'TEXT-LARGE' => 'text', + 'BOOLEAN-' => 'SMALLINT', + 'BOOLEAN-MEDIUM' => 'SMALLINT', + 'TIMESTAMP-' => 'datetime', + ) + ); + } + + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'CHANGE': + // table-name, schema-from, schema-to, (add, alter, remove)+ + for ($i = 3; $i < count($child); $i++) { + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + } + $output .= $this->generateSchemaUpdate($child); + + break; + + case 'REMOVE': + if (!isset($parent['name'])) { + $output .= 'DROP TABLE DB_TABLE_PREFIX' . $node['child'][0]['content'] . ";\n\n"; + + if ($node['child'][0]['content'] != 'Schema') { + $output .= "DELETE FROM DB_TABLE_PREFIXSchema WHERE DB_COLUMN_PREFIXname='" . + $node['child'][0]['content'] . "';\n\n"; + } + } elseif ($parent['name'] == 'CHANGE') { + // (column-name, key, index)+ + for ($i = 0; $i < count($child); $i++) { + $c = $child[$i]; + + switch ($c['name']) { + case 'COLUMN-NAME': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' DROP COLUMN DB_COLUMN_PREFIX' . $c['content']; + $output .= ";\n\n"; + + break; + + case 'KEY': + if (empty($c['attrs']['PRIMARY'])) { + $crc = $this->getIndexCrc($c['child']); + $output .= 'DROP INDEX DB_TABLE_PREFIX' . + $parent['child'][0]['content'] . '_' . $crc . ";\n\n"; + } else { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . + $parent['child'][0]['content'] . ' DROP CONSTRAINT DB_TABLE_PREFIX' + . $parent['child'][0]['content'] . "_pkey;\n\n"; + } + + break; + + case 'INDEX': + // column-name + $output .= 'DROP INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($c['child']); + } + $output .= ";\n\n"; + + break; + + default: + $output .= "5. UNIMPLEMENTED: REMOVE $c[name]\n"; + } + } + } + + break; + + case 'ADD': + // (column, key, index)+ + foreach ($child as $c) { + switch ($c['name']) { + case 'COLUMN': + /* Add a new column, optionally with a default value and a not null constraint + * In PG7, we can not set the default value in the add column statement + * (PG8 doesn't have this limitation though). Therefore do it in 3 steps: + * 1. Add the column without any options. + * 2. Set the default value (only affects future rows) and add the default + * value for existing rows. + * 3. Add the not-null constraint + */ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' ADD COLUMN DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($c['child'], false, false); + $output .= ";\n\n"; + + $defaultValue = $this->getDefaultElement($c['child']); + + if (isset($defaultValue)) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' ALTER COLUMN DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= " SET DEFAULT '$defaultValue';\n\n"; + + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' SET DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= " = '$defaultValue';\n\n"; + } + + if ($this->getNotNullElement($c['child'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ALTER DB_COLUMN_PREFIX' . $c['child'][0]['content'] . + " SET NOT NULL;\n\n"; + } + + break; + + case 'KEY': + // column-name+ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD '; + + if (!empty($c['attrs']['PRIMARY'])) { + $output .= 'PRIMARY KEY('; + } else { + $output .= 'UNIQUE KEY('; + } + + for ($i = 0; $i < count($c['child']); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $c['child'][$i]['content']; + + if ($i < count($c['child']) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + case 'INDEX': + // column-name + $output .= 'CREATE INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $c['child']; + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($columns); + } + $output .= ' ON ' . 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + default: + $output .= "6. UNIMPLEMLENTED: ADD $c[name]\n"; + } + } + + break; + + case 'TABLE': + // table-name, schema, column+, (key | index) + $output .= 'CREATE TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "(\n"; + + for ($i = 2; $i < count($child); $i++) { + if ($child[$i]['name'] != 'COLUMN') { + $output .= "\n"; + + break; + } + + if ($i > 2) { + $output .= ",\n"; + } + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + $firstNonColumn = $i + 1; + } + $output .= ");\n\n"; + + for ($i = $firstNonColumn; $i < count($child); $i++) { + if ($child[$i]['name'] == 'INDEX') { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE INDEX DB_TABLE_PREFIX' . $child[0]['content'] . '_' . $crc . + ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else /* key */ { + if (!empty($child[$i]['attrs']['PRIMARY'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $child[0]['content'] . + ' ADD PRIMARY KEY ('; + $columns = $child[$i]['child']; + + for ($j = 0; $j < count($columns); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$j]['content']; + + if ($j < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE UNIQUE INDEX DB_TABLE_PREFIX' . $child[0]['content'] . + '_' . $crc . ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } + } + } + + // Schema info + $output .= $this->createSql($child[1], 0, 0, $node); + + break; + + case 'ALTER': + // column+ + for ($i = 0; $i < count($child); $i++) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp'; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ";\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' = CAST(DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ' AS ' . + $this->columnDefinition($child[$i]['child'], false) . ");\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' RENAME DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' to DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + + if ($this->getNotNullElement($child[$i]['child'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ALTER DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + " SET NOT NULL;\n\n"; + } + } + + break; + + default: + $output .= parent::createSql($node, $index, $lastPeerIndex, $parent); + } + + return $output; + } + + public function getDbType() { + return 'postgres'; + } +} + +class OracleGenerator extends BaseGenerator { + public function __construct() { + $this->setColumnDefinitionMap( + array( + 'INTEGER-' => 'INTEGER', + 'INTEGER-MEDIUM' => 'INTEGER', + 'INTEGER-LARGE' => 'INTEGER', + 'BIT-LARGE' => 'INTEGER', + 'BIT-MEDIUM' => 'INTEGER', + 'STRING-SMALL' => 'VARCHAR2(32)', + 'STRING-MEDIUM' => 'VARCHAR2(128)', + 'STRING-LARGE' => 'VARCHAR2(255)', + 'TEXT-SMALL' => 'VARCHAR2(4000)', + 'TEXT-' => 'CLOB', + 'TEXT-MEDIUM' => 'CLOB', + 'TEXT-LARGE' => 'CLOB', + 'BOOLEAN-' => 'NUMBER(1)', + 'BOOLEAN-MEDIUM' => 'NUMBER(1)', + 'TIMESTAMP-' => 'datetime', + ) + ); + } + + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'CHANGE': + // table-name, schema-from, schema-to, (add, alter, remove)+ + for ($i = 3; $i < count($child); $i++) { + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + } + $output .= $this->generateSchemaUpdate($child); + + break; + + case 'REMOVE': + if (!isset($parent['name'])) { + $output .= 'DROP TABLE DB_TABLE_PREFIX' . $node['child'][0]['content'] . ";\n\n"; + + if ($node['child'][0]['content'] != 'Schema') { + $output .= "DELETE FROM DB_TABLE_PREFIXSchema WHERE DB_COLUMN_PREFIXname='" . + $node['child'][0]['content'] . "';\n\n"; + } + } elseif ($parent['name'] == 'CHANGE') { + // (column-name, key, index)+ + foreach ($child as $c) { + switch ($c['name']) { + case 'COLUMN-NAME': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= "\n" . ' DROP (DB_COLUMN_PREFIX' . $c['content'] . ')'; + + break; + + case 'KEY': + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + + if (isset($child[0]['attrs']['PRIMARY'])) { + $output .= "\n DROP PRIMARY KEY"; + } else { + $keyColumns = array(); + + foreach ($c['child'] as $keyColumn) { + $keyColumns[] = 'DB_COLUMN_PREFIX' . $keyColumn['content']; + } + $output .= "\n" . ' DROP UNIQUE (' . implode(', ', $keyColumns) . ')'; + } + + break; + + case 'INDEX': + // column-name + $output .= ' DROP INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + + if (isset($child[0]['attrs'][$nameKey])) { + $output .= $child[0]['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($c['child']); + } + + break; + + default: + $output .= "7. UNIMPLEMENTED: REMOVE $c[name]\n"; + } + $output .= ";\n\n"; + } + } + + break; + + case 'ADD': + // (column, key, index)+ + for ($k = 0; $k < count($child); $k++) { + $c = $child[$k]; + + switch ($c['name']) { + case 'COLUMN': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= "\n" . ' ADD (DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($c['child']) . ')'; + + break; + + case 'KEY': + // column-name+ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= "\n ADD "; + + if (!empty($c['attrs']['PRIMARY'])) { + $output .= 'PRIMARY KEY('; + } else { + $output .= 'UNIQUE KEY('; + } + + for ($i = 0; $i < count($c['child']); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $c['child'][$i]['content']; + + if ($i < count($c['child']) - 1) { + $output .= ', '; + } + } + $output .= ')'; + + break; + + case 'INDEX': + // column-name + $output .= 'CREATE INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $c['child']; + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($columns); + } + $output .= ' ON DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + + break; + + default: + $output .= "8. UNIMPLEMLENTED: ADD $c[name]\n"; + } + $output .= ";\n\n"; + } + + break; + + case 'TABLE': + // table-name, schema, column+, (key | index) + $output .= 'CREATE TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "(\n"; + + for ($i = 2; $i < count($child); $i++) { + if ($child[$i]['name'] != 'COLUMN') { + $output .= "\n"; + + break; + } + + if ($i > 2) { + $output .= ",\n"; + } + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + $firstNonColumn = $i + 1; + } + $output .= ");\n\n"; + + $keyColumns = array(); + + for ($i = $firstNonColumn; $i < count($child); $i++) { + if ($child[$i]['name'] == 'INDEX') { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE INDEX DB_TABLE_PREFIX' . $child[0]['content'] . '_' . $crc . + "\n " . ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else { + $keys[] = $child[$i]; + } + } + + if (!empty($keys)) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "\n"; + + foreach ($keys as $key) { + if (!empty($key['attrs']['PRIMARY'])) { + $output .= ' ADD PRIMARY KEY ('; + } else { + $output .= ' ADD UNIQUE ('; + } + + for ($i = 0; $i < count($key['child']); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $key['child'][$i]['content']; + + if ($i < count($key['child']) - 1) { + $output .= ', '; + } + } + $output .= ")\n"; + } + $output .= ";\n\n"; + } + + // Schema info + $output .= $this->createSql($child[1], 0, 0, $node); + + break; + + case 'COLUMN': + // column-name, column-type, column-size, not-null? + $output .= ' DB_COLUMN_PREFIX' . $child[0]['content']; + $output .= ' ' . $this->columnDefinition($child, false); + + if (($notNull = $this->getNotNullElement($child)) + && (empty($notNull['attrs']['EMPTY']) || $notNull['attrs']['EMPTY'] != 'allowed') + ) { + $output .= ' NOT NULL'; + } + + break; + + case 'ALTER': + // column+ + for ($i = 0; $i < count($child); $i++) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD (DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp'; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ");\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' = DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP (DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ");\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' RENAME COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' TO DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + + if (($notNull = $this->getNotNullElement($child[$i]['child'])) + && (empty($notNull['attrs']['EMPTY']) + || $notNull['attrs']['EMPTY'] != 'allowed') + ) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' MODIFY (DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + " NOT NULL);\n\n"; + } + } + + break; + + default: + $output .= parent::createSql($node, $index, $lastPeerIndex, $parent); + } + + return $output; + } + + public function getDbType() { + return 'oracle'; + } +} + +/** + * Notes regarding DB2 limitations on Table and Index names: + * + * DB2 currently limits the length of table names to 30 characters, and index names to 18 + * characters. We don't have to worry about the 30 character table name problem because we force + * table names to be shorter than this in GalleryStorage (and it's very important that the table + * names we choose here match up with the ones that GalleryStorage expects). However we have + * (and need) no such provision for indexes because this is the only place where we define index + * names. + * + * The installer "database setup" step prefixes all tables and indexes with "gtst#" (5 chars). + * The installer default is "g2_" (3 chars). So if we allow room for a 5 char prefix, that + * leaves us 13 characters for an 18-character index name. Our index CRC values are another 5 + * characters. That leaves us 8 characters to use for a descriptive index name. I don't know if + * DB2 index names are required to be unique in the database or just to the table so to avoid any + * risks we can't just use a prefix or suffix of the table name because it may overlap with + * another similar table name. + * + * So for indexes we'll use the following format: + * DB_TABLE_PREFIX + substr(table name, 0, 5) + substr(md5(table name), -2) + '_' + index crc + * + * That works out to: + * <= 5 chars + 5 + 2 + 1 + 5 = <= 18 + */ +class Db2Generator extends BaseGenerator { + public function __construct() { + // The column size is limited to 32kbyte + $this->setColumnDefinitionMap( + array( + 'INTEGER-' => 'INTEGER', + 'INTEGER-MEDIUM' => 'INTEGER', + 'INTEGER-LARGE' => 'INTEGER', + 'BIT-LARGE' => 'VARCHAR(32) FOR BIT DATA', + 'BIT-MEDIUM' => 'VARCHAR(32) FOR BIT DATA', + 'STRING-SMALL' => 'VARCHAR(32)', + 'STRING-MEDIUM' => 'VARCHAR(128)', + 'STRING-LARGE' => 'VARCHAR(255)', + 'TEXT-SMALL' => 'VARCHAR(10000)', + 'TEXT-' => 'VARCHAR(15000)', + 'TEXT-MEDIUM' => 'VARCHAR(15000)', + 'TEXT-LARGE' => 'CLOB(2G) NOT LOGGED', + 'BOOLEAN-' => 'SMALLINT', + 'BOOLEAN-MEDIUM' => 'SMALLINT', + 'TIMESTAMP-' => 'datestamp', + ) + ); + } + + public function columnDefinition($child, $includeNotNull = true, $includeDefault = true) { + $output = parent::columnDefinition($child, $includeNotNull, false); + + // DB2 -> Make sure DEFAULT expression doesn't have quotes for numeric + if ($includeDefault) { + $defaultValue = $this->getDefaultElement($child); + + if (isset($defaultValue)) { + if ($child[1]['content'] != 'INTEGER' && $child[1]['content'] != 'BOOLEAN') { + $defaultValue = "'$defaultValue'"; + } + $output .= " DEFAULT $defaultValue"; + } + } + + return $output; + } + + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'CHANGE': + // table-name, schema-from, schema-to, (add, alter, remove)+ + for ($i = 3; $i < count($child); $i++) { + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + } + $output .= $this->generateSchemaUpdate($child); + + break; + + case 'REMOVE': + if (!isset($parent['name'])) { + $output .= 'DROP TABLE DB_TABLE_PREFIX' . $node['child'][0]['content'] . ";\n\n"; + + if ($node['child'][0]['content'] != 'Schema') { + $output .= "DELETE FROM DB_TABLE_PREFIXSchema WHERE DB_COLUMN_PREFIXname='" . + $node['child'][0]['content'] . "';\n\n"; + } + } elseif ($parent['name'] == 'CHANGE') { + // (column-name, key, index)+ + for ($i = 0; $i < count($child); $i++) { + $c = $child[$i]; + + switch ($c['name']) { + case 'COLUMN-NAME': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' DROP COLUMN DB_COLUMN_PREFIX' . $c['content'] . ";\n\n"; + $output .= "CALL ADMIN_CMD ('REORG TABLE DB_TABLE_PREFIX"; + $output .= $parent['child'][0]['content'] . "');\n\n"; + + break; + + case 'KEY': + if (empty($c['attrs']['PRIMARY'])) { + $crc = $this->getIndexCrc($c['child']); + $output .= 'DROP INDEX DB_TABLE_PREFIX' . + $parent['child'][0]['content'] . '_' . $crc . ";\n\n"; + } else { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . + $parent['child'][0]['content'] . " DROP PRIMARY KEY;\n\n"; + } + + break; + + case 'INDEX': + // column-name + $output .= 'DROP INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . + substr($parent['child'][0]['content'], 0, 5) . + substr(md5($parent['child'][0]['content']), -2) . + '_' . $this->getIndexCrc($c['child']); + } + $output .= ";\n\n"; + + break; + + default: + $output .= "5. UNIMPLEMENTED: REMOVE $c[name]\n"; + } + } + } + + break; + + case 'ADD': + // (column, key, index)+ + foreach ($child as $c) { + switch ($c['name']) { + case 'COLUMN': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' ADD COLUMN DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($c['child']); + $output .= ";\n\n"; + + break; + + case 'KEY': + // column-name+ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD '; + + if (!empty($c['attrs']['PRIMARY'])) { + $output .= 'PRIMARY KEY('; + } else { + $output .= 'UNIQUE KEY('; + } + + for ($i = 0; $i < count($c['child']); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $c['child'][$i]['content']; + + if ($i < count($c['child']) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + case 'INDEX': + // column-name + $output .= 'CREATE INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $c['child']; + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . + substr($parent['child'][0]['content'], 0, 5) . + substr(md5($parent['child'][0]['content']), -2) . + '_' . $this->getIndexCrc($c['child']); + } + $output .= ' ON ' . 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + default: + $output .= "6. UNIMPLEMLENTED: ADD $c[name]\n"; + } + } + + break; + + case 'TABLE': + // table-name, schema, column+, (key | index) + $output .= 'CREATE TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "(\n"; + + for ($i = 2; $i < count($child); $i++) { + if ($child[$i]['name'] != 'COLUMN') { + $output .= "\n"; + + break; + } + + if ($i > 2) { + $output .= ",\n"; + } + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + $firstNonColumn = $i + 1; + } + $output .= ");\n\n"; + + for ($i = $firstNonColumn; $i < count($child); $i++) { + if ($child[$i]['name'] == 'INDEX') { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE INDEX DB_TABLE_PREFIX' . + substr($child[0]['content'], 0, 5) . + substr(md5($child[0]['content']), -2) . '_' . $crc . + "\n " . ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else /* key */ { + if (!empty($child[$i]['attrs']['PRIMARY'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $child[0]['content'] . + ' ADD PRIMARY KEY ('; + $columns = $child[$i]['child']; + + for ($j = 0; $j < count($columns); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$j]['content']; + + if ($j < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE UNIQUE INDEX DB_TABLE_PREFIX' . + substr($child[0]['content'], 0, 5) . + substr(md5($child[0]['content']), -2) . '_' . $crc . + " \n" . ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } + } + } + + // Schema info + $output .= $this->createSql($child[1], 0, 0, $node); + + break; + + case 'ALTER': + // column+ + for ($i = 0; $i < count($child); $i++) { + // DB2's "ALTER TABLE ALTER COLUMN" is somewhat limited. Use a workaround. + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp'; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ";\n\n"; + // Omit the CAST when the target type is CLOB to avoid invalid SQL state. + $targetType = $this->columnDefinition($child[$i]['child'], false); + $copyFrom = 'DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content']; + + if (strpos($targetType, 'CLOB') === false) { + $copyFrom = 'CAST(' . $copyFrom . ' AS ' . $targetType . ')'; + } + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' = ' . $copyFrom . ";\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + /* + * DROP COLUMN puts the table into a state that requires REORG TABLE before + * it can be accessed again. + */ + $output .= "CALL ADMIN_CMD ('REORG TABLE DB_TABLE_PREFIX" . + $parent['child'][0]['content'] . "');\n\n"; + // DB2 can't rename columns + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ";\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + ' = DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . "Temp;\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + "Temp;\n\n"; + $output .= "CALL ADMIN_CMD ('REORG TABLE DB_TABLE_PREFIX" . + $parent['child'][0]['content'] . "');\n\n"; + + if ($this->getNotNullElement($child[$i]['child'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ALTER DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + " SET NOT NULL;\n\n"; + } + } + + break; + + default: + $output .= parent::createSql($node, $index, $lastPeerIndex, $parent); + } + + return $output; + } + + public function getDbType() { + return 'db2'; + } +} + +class MSSqlGenerator extends BaseGenerator { + public function __construct() { + $this->setColumnDefinitionMap( + array( + 'INTEGER-' => 'INT', + 'INTEGER-MEDIUM' => 'INT', + 'INTEGER-LARGE' => 'INT', + 'BIT-LARGE' => 'INT', + 'BIT-MEDIUM' => 'INT', + 'STRING-SMALL' => 'NVARCHAR(32)', + 'STRING-MEDIUM' => 'NVARCHAR(128)', + 'STRING-LARGE' => 'NVARCHAR(255)', + 'TEXT-SMALL' => 'NVARCHAR(MAX)', + 'TEXT-' => 'NVARCHAR(MAX)', + 'TEXT-MEDIUM' => 'NVARCHAR(MAX)', + 'TEXT-LARGE' => 'NVARCHAR(MAX)', + 'BOOLEAN-' => 'BIT', + 'BOOLEAN-MEDIUM' => 'BIT', + 'TIMESTAMP-' => 'datetime', + ) + ); + } + + public function columnDefinition($child, $includeNotNull = true, $includeDefault = true) { + $output = parent::columnDefinition($child, $includeNotNull, $includeDefault); + + if ($includeNotNull && !$this->getNotNullElement($child)) { + $output .= ' NULL'; + } + + return $output; + } + + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'CHANGE': + // table-name, schema-from, schema-to, (add, alter, remove)+ + for ($i = 3; $i < count($child); $i++) { + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + } + $output .= $this->generateSchemaUpdate($child); + + break; + + case 'REMOVE': + if (!isset($parent['name'])) { + $output .= 'DROP TABLE DB_TABLE_PREFIX' . $node['child'][0]['content'] . ";\n\n"; + + if ($node['child'][0]['content'] != 'Schema') { + $output .= "DELETE FROM DB_TABLE_PREFIXSchema WHERE DB_COLUMN_PREFIXname='" . + $node['child'][0]['content'] . "';\n\n"; + } + } elseif ($parent['name'] == 'CHANGE') { + // (column-name, key, index)+ + for ($i = 0; $i < count($child); $i++) { + $c = $child[$i]; + + switch ($c['name']) { + case 'COLUMN-NAME': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' DROP COLUMN DB_COLUMN_PREFIX' . $c['content']; + $output .= ";\n\n"; + + break; + + case 'KEY': + if (empty($c['attrs']['PRIMARY'])) { + $crc = $this->getIndexCrc($c['child']); + $output .= 'DROP INDEX DB_TABLE_PREFIX' . + $parent['child'][0]['content'] . '_' . $crc . ";\n\n"; + } else { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . + $parent['child'][0]['content'] . ' DROP CONSTRAINT DB_TABLE_PREFIX' + . $parent['child'][0]['content'] . "_pkey;\n\n"; + } + + break; + + case 'INDEX': + // column-name + $output .= 'DROP INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '.DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($c['child']); + } + $output .= ";\n\n"; + + break; + + default: + $output .= "5. UNIMPLEMENTED: REMOVE $c[name]\n"; + } + } + } + + break; + + case 'ADD': + // (column, key, index)+ + foreach ($child as $c) { + switch ($c['name']) { + case 'COLUMN': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' ADD DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($c['child']); + $output .= ";\n\n"; + + break; + + case 'KEY': + // column-name+ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD '; + + if (!empty($c['attrs']['PRIMARY'])) { + $output .= 'PRIMARY KEY('; + } else { + $output .= 'UNIQUE KEY('; + } + + for ($i = 0; $i < count($c['child']); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $c['child'][$i]['content']; + + if ($i < count($c['child']) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + case 'INDEX': + // column-name + $output .= 'CREATE INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $c['child']; + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + '_' . $this->getIndexCrc($columns); + } + $output .= ' ON ' . 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + default: + $output .= "6. UNIMPLEMLENTED: ADD $c[name]\n"; + } + } + + break; + + case 'TABLE': + // table-name, schema, column+, (key | index) + $output .= 'CREATE TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "(\n"; + + for ($i = 2; $i < count($child); $i++) { + if ($child[$i]['name'] != 'COLUMN') { + $output .= "\n"; + + break; + } + + if ($i > 2) { + $output .= ",\n"; + } + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + $firstNonColumn = $i + 1; + } + $output .= ");\n\n"; + + for ($i = $firstNonColumn; $i < count($child); $i++) { + if ($child[$i]['name'] == 'INDEX') { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE INDEX DB_TABLE_PREFIX' . $child[0]['content'] . '_' . $crc . + ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else /* key */ { + if (!empty($child[$i]['attrs']['PRIMARY'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $child[0]['content'] . + ' ADD PRIMARY KEY ('; + $columns = $child[$i]['child']; + + for ($j = 0; $j < count($columns); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$j]['content']; + + if ($j < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } else { + $crc = $this->getIndexCrc($child[$i]['child']); + $output .= 'CREATE UNIQUE INDEX DB_TABLE_PREFIX' . $child[0]['content'] . + '_' . $crc . ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } + } + } + + // Schema info + $output .= $this->createSql($child[1], 0, 0, $node); + + break; + + case 'ALTER': + // column+ + for ($i = 0; $i < count($child); $i++) { + // MSSQL can't add defaults when altering columns. Use a workaround. + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp'; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ";\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' = CAST(DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ' AS ' . + $this->columnDefinition($child[$i]['child'], false) . ");\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + // MSSQL can't rename columns + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ";\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + ' = DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . "Temp;\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + "Temp;\n\n"; + + if ($this->getNotNullElement($child[$i]['child'])) { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ALTER COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + ' ' . $this->columnDefinition($child[$i]['child'], true, false) . ";\n\n"; + } + } + + break; + + default: + $output .= parent::createSql($node, $index, $lastPeerIndex, $parent); + } + + return $output; + } + + public function getDbType() { + return 'mssql'; + } +} + +class SQLiteGenerator extends BaseGenerator { + public function __construct() { + $this->setColumnDefinitionMap( + array( + 'INTEGER-' => 'INTEGER', + 'INTEGER-MEDIUM' => 'INTEGER', + 'INTEGER-LARGE' => 'INTEGER', + 'BIT-LARGE' => 'TEXT', + 'BIT-MEDIUM' => 'TEXT', + 'STRING-SMALL' => 'TEXT', + 'STRING-MEDIUM' => 'TEXT', + 'STRING-LARGE' => 'TEXT', + 'TEXT-SMALL' => 'TEXT', + 'TEXT-' => 'TEXT', + 'TEXT-MEDIUM' => 'TEXT', + 'TEXT-LARGE' => 'TEXT', + 'BOOLEAN-' => 'INTEGER', + 'BOOLEAN-MEDIUM' => 'INTEGER', + 'TIMESTAMP-' => 'INTEGER', + ) + ); + } + + public function columnDefinition($child, $includeNotNull = true, $includeDefault = true) { + $output = parent::columnDefinition($child, $includeNotNull, false); + + if ($includeDefault) { + $defaultValue = $this->getDefaultElement($child); + + if (isset($defaultValue)) { + $output .= " DEFAULT '$defaultValue'"; + } + } + + return $output; + } + + public function createSql($node, $index, $lastPeerIndex, $parent) { + $output = ''; + + $child = $node['child'] = isset($node['child']) ? $node['child'] : array(); + + switch ($node['name']) { + case 'TABLE': + // table-name, schema, column+, (key | index) + $output .= 'CREATE TABLE DB_TABLE_PREFIX' . $child[0]['content'] . "(\n"; + + for ($i = 2; $i < count($child); $i++) { + if ($child[$i]['name'] != 'COLUMN') { + $output .= "\n"; + + break; + } + + if ($i > 2) { + $output .= ",\n"; + } + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + $firstNonColumn = $i + 1; + } + $output .= ");\n\n"; + + for ($i = $firstNonColumn; $i < count($child); $i++) { + if ($child[$i]['name'] == 'INDEX') { + // column-name + $output .= 'CREATE INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $child[$i]['child']; + + if (isset($child[$i]['attrs'][$nameKey])) { + $output .= $child[$i]['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $child[0]['content'] + . '_' . $this->getIndexCrc($columns); + } + $output .= ' ON ' . 'DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($columns); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$j]['content']; + + if ($j < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + } + $output .= 'CREATE UNIQUE INDEX DB_TABLE_PREFIX' . $child[0]['content']; + + if (!empty($child[$i]['attrs']['PRIMARY'])) { + $output .= '_pkey'; + } else { + $output .= '_' . $this->getIndexCrc($child[$i]['child']); + } + $output .= ' ON DB_TABLE_PREFIX' . $child[0]['content'] . '('; + + for ($j = 0; $j < count($child[$i]['child']); $j++) { + $output .= 'DB_COLUMN_PREFIX' . $child[$i]['child'][$j]['content']; + + if ($j < count($child[$i]['child']) - 1) { + $output .= ', '; + } + } + $output .= ");\n\n"; + } + + // Schema info + $output .= $this->createSql($child[1], 0, 0, $node); + + break; + + case 'CHANGE': + // table-name, schema-from, schema-to, (add, alter, remove)+ + for ($i = 3; $i < count($child); $i++) { + $output .= $this->createSql($child[$i], $i, count($child) - 1, $node); + } + $output .= $this->generateSchemaUpdate($child); + + break; + + case 'ADD': + // (column, key, index)+ + foreach ($child as $c) { + switch ($c['name']) { + case 'COLUMN': + // column-name + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' ADD DB_COLUMN_PREFIX' . $c['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($c['child']); + $output .= ";\n"; + $output .= 'VACUUM DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ";\n\n"; + + break; + + case 'KEY': + $output .= 'CREATE UNIQUE INDEX '; + $columns = $c['child']; + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '_'; + + if (!empty($c['attrs']['PRIMARY'])) { + $output .= 'pkey'; + } else { + $output .= $this->getIndexCrc($columns); + } + $output .= ' ON ' . 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + case 'INDEX': + // column-name + $output .= 'CREATE INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + $columns = $c['child']; + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] + . '_' . $this->getIndexCrc($columns); + } + $output .= ' ON ' . 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] . '('; + + for ($i = 0; $i < count($columns); $i++) { + $output .= 'DB_COLUMN_PREFIX' . $columns[$i]['content']; + + if ($i < count($columns) - 1) { + $output .= ', '; + } + } + $output .= ')'; + $output .= ";\n\n"; + + break; + + default: + $output .= "6. UNIMPLEMLENTED: ADD $c[name]\n"; + } + } + + break; + + case 'REMOVE': + if (!isset($parent['name'])) { + $output .= 'DROP TABLE DB_TABLE_PREFIX' . $node['child'][0]['content'] . ";\n\n"; + + if ($node['child'][0]['content'] != 'Schema') { + $output .= "DELETE FROM DB_TABLE_PREFIXSchema WHERE DB_COLUMN_PREFIXname='" + . $node['child'][0]['content'] . "';\n\n"; + } + } elseif ($parent['name'] == 'CHANGE') { + // (column-name, key, index)+ + for ($i = 0; $i < count($child); $i++) { + $c = $child[$i]; + + switch ($c['name']) { + case 'COLUMN-NAME': + /** + * @todo Find a better way to handle DROP COLUMN. The below code doesn't + * work in SQLite 3 and our adodb driver intercepts and handles it instead. + */ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content']; + $output .= ' DROP COLUMN DB_COLUMN_PREFIX' . $c['content']; + $output .= ";\n\n"; + + break; + + case 'KEY': + if (empty($c['attrs']['PRIMARY'])) { + $crc = $this->getIndexCrc($c['child']); + $output .= 'DROP INDEX ' . $parent['child'][0]['content'] . '_' . $crc + . ";\n\n"; + } else { + $output .= 'ALTER TABLE DB_TABLE_PREFIX' + . $parent['child'][0]['content'] + . ' DROP CONSTRAINT DB_TABLE_PREFIX' + . $parent['child'][0]['content'] . "_pkey;\n\n"; + } + + break; + + case 'INDEX': + // column-name + $output .= 'DROP INDEX '; + $nameKey = strtoupper('name_' . $this->getDbType()); + + if (isset($c['attrs'][$nameKey])) { + $output .= $c['attrs'][$nameKey]; + } else { + $output .= 'DB_TABLE_PREFIX' . $parent['child'][0]['content'] + . '_' . $this->getIndexCrc($c['child']); + } + $output .= ";\n\n"; + + break; + + default: + $output .= "5. UNIMPLEMENTED: REMOVE $c[name]\n"; + } + } + } + + break; + + case 'ALTER': + // column+ + for ($i = 0; $i < count($child); $i++) { + /* + * SQLite only supports ADD COLUMN and the workaround DROP COLUMN. + * As a workaround for ALTER COLUMN: + * Create Temporary Table + * Copy current column to temporary + * Delete current column + * Recreate Column with new settings + * Copy content back to current column + * Delete Temporary Table + */ + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp'; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false) . ";\n\n"; + $output .= 'VACUUM DB_TABLE_PREFIX' . $parent['child'][0]['content'] . ";\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . 'Temp' . + ' = DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . ";\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' ADD COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content']; + $output .= ' ' . $this->columnDefinition($child[$i]['child'], false, false); + $defaultValue = $this->getDefaultElement($child[$i]['child']); + + if (($notNull = $this->getNotNullElement($child[$i]['child'])) + && (empty($notNull['attrs']['EMPTY']) + || $notNull['attrs']['EMPTY'] != 'allowed') + ) { + $output .= " NOT NULL DEFAULT '" . (isset($defaultValue) ? $defaultValue : '') . "'"; + } elseif ($defaultValue !== null) { + $output .= " DEFAULT '" . $defaultValue . "'"; + } + $output .= ";\n\n"; + $output .= 'VACUUM DB_TABLE_PREFIX' . $parent['child'][0]['content'] . ";\n\n"; + $output .= 'UPDATE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' SET DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . + ' = DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . "Temp;\n\n"; + $output .= 'ALTER TABLE DB_TABLE_PREFIX' . $parent['child'][0]['content'] . + ' DROP COLUMN DB_COLUMN_PREFIX' . $child[$i]['child'][0]['content'] . "Temp;\n\n"; + } + + break; + + default: + $output .= parent::createSql($node, $index, $lastPeerIndex, $parent); + } + + return $output; + } + + public function getDbType() { + return 'sqlite'; + } +} diff --git a/lib/tools/bin/getIllegalFunctions.pl b/lib/tools/bin/getIllegalFunctions.pl new file mode 100644 index 00000000..df208e7c --- /dev/null +++ b/lib/tools/bin/getIllegalFunctions.pl @@ -0,0 +1,51 @@ +#!/usr/local/bin/perl +# +use strict; + +my $versionXmlUrl = 'http://cvs.php.net/co.php/phpdoc/xsl/version.xml?r=1.16&p=1'; +my $versionXmlFile = '/tmp/version.xml'; + +unless (-f $versionXmlFile) { + system("wget -O $versionXmlFile $versionXmlUrl") and die "unable to wget $versionXmlUrl"; +} + +open(FD, "<$versionXmlFile") || die; + +print ") { + # only get functions + next unless /\/g; + s/</ or >= 4.0* is fine + next if ($comparison =~ />=?4\.0/); + + # Anything >= 4.1.0 is fine + next if ($comparison =~ />=4\.1\.0/); + + printf('%-70s // %s', "\$illegalFunctions[] = '$function';", $comparison); + print "\n"; +} + +close(FD); + +print "?>\n"; diff --git a/lib/tools/bin/makeManifest.php b/lib/tools/bin/makeManifest.php new file mode 100644 index 00000000..940cf6c6 --- /dev/null +++ b/lib/tools/bin/makeManifest.php @@ -0,0 +1,275 @@ +#!/usr/bin/php -q + $entries) { + if (!file_exists($baseDir . $manifest)) { + $oldLines = array(); + $oldContent = $oldRevision = ''; + $nl = DIRECTORY_SEPARATOR == '\\' ? "\r\n" : "\n"; + } else { + $oldLines = file($baseDir . $manifest); + $oldContent = implode('', $oldLines); + $nl = preg_match('/\r\n/', $oldContent) ? "\r\n" : "\n"; + $matches = array(); + $oldRevision = preg_match('/Revision: (\d+\s*)\$/', $oldLines[0], $matches) ? $matches[1] : ''; + } + + $newContent = '# $Revi' . "sion: $oldRevision\$$nl"; + $newContent .= "# File crc32 crc32(crlf) size size(crlf) or R File$nl"; + + $deleted = $seen = array(); + + foreach ($entries as $entry) { + list($file, $isBinary) = preg_split('/\@\@/', $entry); + $relativeFilePath = $file; + $file = $baseDir . $file; + + if (preg_match('/deleted:(.*)/', $relativeFilePath, $matches)) { + $deleted[$matches[1]] = true; + } else { + $seen[$relativeFilePath] = true; + $fileHandle = fopen($file, 'rb'); + $fileSize = filesize($file); + $data = fread($fileHandle, $fileSize); + fclose($fileHandle); + + $data_crlf = $data; + + if ($isBinary) { + $size = $size_crlf = filesize($file); + } else { + if (preg_match("/\r\n/", $data)) { + $data = str_replace("\r\n", "\n", $data); + } else { + $data_crlf = str_replace("\n", "\r\n", $data_crlf); + } + $size = strlen($data); + $size_crlf = strlen($data_crlf); + } + + $cksum = crc32($data); + $cksum_crlf = crc32($data_crlf); + $newContent .= sprintf( + "$relativeFilePath\t%u\t%u\t%d\t%d$nl", + $cksum, + $cksum_crlf, + $size, + $size_crlf + ); + } + } + + if (!empty($oldLines)) { + foreach ($oldLines as $line) { + if ($line[0] == '#') { + continue; + } + + if (preg_match('/^R\t(.*)$/', $line, $matches)) { + $file = trim($matches[1]); + + if (empty($seen[$file])) { + $deleted[$file] = true; + } + } else { + preg_match('/^(.+?)\t/', $line, $matches); + $file = trim($matches[1]); + + if (empty($seen[$file])) { + $deleted[$file] = true; + } + } + } + + foreach ($deleted as $file => $unused) { + $newContent .= "R\t$file$nl"; + } + } + + if ($oldContent != $newContent) { + file_put_contents($baseDir . $manifest, $newContent); + $changed++; + } + $total++; + } + + quiet_print(sprintf('Completed in %d seconds', time() - $startTime)); + quiet_print(sprintf("Manifests changed: $changed (total: $total)")); +} + +/** + * Retrieve the SVN Entries + * @param string $filterpath Path to create retrieve the SVN entries for. + * @return array List of SVN entries + */ +function listSvn($filterpath) { + $entries = array(); + + $binaryList = array(); + exec("svn propget --non-interactive -R svn:mime-type $filterpath", $output); + + foreach ($output as $line) { + $parts = preg_split('/\s-\s/', $line); + $file = str_replace('\\', '/', $parts[0]); + $binaryList[$file] = 1; + } + + $output = array(); + exec("svn status --non-interactive -v -q $filterpath", $output); + + foreach ($output as $line) { + $matches = array(); + + if (preg_match('/^(.).....\s*\d+\s+[\d|\?]+\s+\S+\s+(.*)$/', $line, $matches) == 0) { + die("Unexpected SVN status format:\n$line\n"); + } + + if (!file_exists($matches[2])) { + die("The file '$matches[2]' does not exist"); + } + + if (is_dir($matches[2])) { + continue; + } + + if (preg_match('#[\\/]MANIFEST#', $matches[2]) > 0) { + continue; + } + + if ($matches[1] == 'M') { + quiet_print("Warning: $matches[2] is locally modified"); + } elseif (!in_array($matches[1], array(' ', 'D', 'M'))) { + die("Check {$matches[1]} status for {$matches[2]}"); + } + + $status = $matches[1] === 'D' ? 'deleted:' : ''; + + $file = str_replace('\\', '/', $matches[2]); + $entries[] = sprintf('%s%s@@%d', $status, $file, isset($binaryList[$file])); + } + + return $entries; +} + +?> diff --git a/lib/tools/bin/maps.tpl b/lib/tools/bin/maps.tpl new file mode 100644 index 00000000..1f9b057a --- /dev/null +++ b/lib/tools/bin/maps.tpl @@ -0,0 +1,7 @@ +array('type'=>{$member.type},'size'=>{$member.size}{if !empty($member.notNull)},'notNull'=>true{/if}{if !empty($member.notNullEmptyAllowed)},'notNullEmptyAllowed'=>true{/if}){if !$smarty.foreach.inner.last},{/if}{/foreach}); +{/foreach} +?> + diff --git a/lib/tools/bin/rebuild-modules.pl b/lib/tools/bin/rebuild-modules.pl new file mode 100755 index 00000000..741542c9 --- /dev/null +++ b/lib/tools/bin/rebuild-modules.pl @@ -0,0 +1,16 @@ +#!/usr/bin/perl +use strict; +chomp(my $CURDIR = `pwd`); +chomp(my $MAKE = `(which gmake || which make) 2>/dev/null`); + +if (!$MAKE) { + die "Unable to locate 'make' or 'gmake'"; +} + +my @MAKEFILES = ; +foreach my $makefile (@MAKEFILES) { + (my $module = $makefile) =~ s|(modules/.*?)/.*|$1|; + print STDERR "Building $module\n"; + chdir("$CURDIR/$module/classes") || die; + system("$MAKE -s clean && $MAKE -s && $MAKE -s clean") and die; +} diff --git a/lib/tools/creator/GNUmakefile.tpl b/lib/tools/creator/GNUmakefile.tpl new file mode 100644 index 00000000..f22e5464 --- /dev/null +++ b/lib/tools/creator/GNUmakefile.tpl @@ -0,0 +1,7 @@ +{if $makefileType == 'classes'} +include ../../../lib/tools/bin/GNUmakefile.classes +{else} +include ../../../../lib/tools/bin/GNUmakefile.GalleryStorage +{/if} + + diff --git a/lib/tools/creator/MyPage.inc.tpl b/lib/tools/creator/MyPage.inc.tpl new file mode 100644 index 00000000..b8cc72ef --- /dev/null +++ b/lib/tools/creator/MyPage.inc.tpl @@ -0,0 +1,106 @@ + $itemId)); + if ($ret) {ldelim} + return array($ret, null); + {rdelim} + + $ret = GalleryCoreApi::addMapEntry( + '{$mapName}', + array('itemId' => $itemId, 'itemValue' => $form['value'])); + if ($ret) {ldelim} + return array($ret, null); + {rdelim} + + /* Send the user to a confirmation page, for now */ + $redirect['view'] = '{$moduleId}.{$viewName}'; + $redirect['itemId'] = (int)$itemId; + $status['added'] = 1; + {rdelim} + + $results['status'] = $status; + $results['error'] = $error; + $results['redirect'] = $redirect; + + return array(null, $results); + {rdelim} +{rdelim} + +/** + * This is a sample page generated by the Gallery 2 module creator. + * + * @package {$ucModuleId} + * @subpackage UserInterface + * + */ +class {$viewName}View extends GalleryView {ldelim} + + /** + * @see GalleryView::loadTemplate + */ + function loadTemplate(&$template = null, &$form = null) {ldelim} + /* Load our item */ + list ($ret, $item) = $this->getItem(); + if ($ret) {ldelim} + return array($ret, null); + {rdelim} + + ${$viewName} = array(); + ${$viewName}['item'] = (array)$item; + GalleryCoreApi::requireOnce('modules/{$moduleId}/classes/{$viewName}Helper.class'); + list ($ret, ${$viewName}['value']) = {$viewName}Helper::getItemValue($item->getId()); + if ($ret) {ldelim} + return array($ret, null); + {rdelim} + + $template->setVariable('{$viewName}', ${$viewName}); + + return array(null, array('body' => 'modules/{$moduleId}/templates/{$viewName}.tpl')); + {rdelim} +{rdelim} +?> diff --git a/lib/tools/creator/MyPage.tpl.tpl b/lib/tools/creator/MyPage.tpl.tpl new file mode 100644 index 00000000..f655122e --- /dev/null +++ b/lib/tools/creator/MyPage.tpl.tpl @@ -0,0 +1,40 @@ + +
+

{ldelim}g->text text="My First Page"{rdelim}

+
+ +
+ Hello, my name is {$authorFullName}. This is my first Gallery 2 page! +
+ +
+ The item you chose for this action was: {ldelim}${$viewName}.item.title|default:${$viewName}.item.pathComponent{rdelim} +
+
+ {ldelim}if empty(${$viewName}.value){rdelim} + There is no value yet for this item. + {ldelim}else{rdelim} + The value in the database for this item is: {ldelim}${$viewName}.value{rdelim} + {ldelim}/if{rdelim} +
+ +
+
+ {ldelim}g->hiddenFormVars{rdelim} + + + +
+ +
+ {ldelim}g->text text="Enter a value for this item:"{rdelim} + +
+ +
+ +
+
+ + diff --git a/lib/tools/creator/MyPageHelper.class.tpl b/lib/tools/creator/MyPageHelper.class.tpl new file mode 100644 index 00000000..7e72ff73 --- /dev/null +++ b/lib/tools/creator/MyPageHelper.class.tpl @@ -0,0 +1,71 @@ +search($query, array($itemId)); + if ($ret) {ldelim} + return array($ret, null); + {rdelim} + + if ($searchResults->resultCount() != 0) {ldelim} + $result = $searchResults->nextResult(); + $data = $result[0]; + {rdelim} else {ldelim} + $data = ''; + {rdelim} + + return array(null, $data); + {rdelim} +{rdelim} +?> diff --git a/lib/tools/creator/create-module.php b/lib/tools/creator/create-module.php new file mode 100755 index 00000000..d57de0e9 --- /dev/null +++ b/lib/tools/creator/create-module.php @@ -0,0 +1,225 @@ +compile_dir = $tmpdir; +$smarty->error_reporting = error_reporting(); +$smarty->debugging = true; +$smarty->use_sub_dirs = false; +$smarty->template_dir = __DIR__; + +// Gather any info we need from the user +if (!empty($author)) { + $defaultModuleName = 'Hello ' . ucfirst($author); +} else { + $defaultModuleName = 'Hello World'; +} + +while (empty($moduleName)) { + $moduleName = ask('What is the name of your module?', $defaultModuleName); +} + +while (empty($moduleId)) { + $moduleId = ask( + 'What is the id of your module?', + strtolower(preg_replace('/ /', '', $moduleName)) + ); +} +$moduleId = preg_replace('/\W/', '', $moduleId); +$ucModuleId = ucfirst($moduleId); + +$smarty->assign('moduleId', $moduleId); +$smarty->assign('ucModuleId', $ucModuleId); +$smarty->assign('moduleName', $moduleName); +$smarty->assign('author', $author); +$smarty->assign('authorFullName', $authorFullName); +$smarty->assign('viewName', $ucModuleId); +$smarty->assign('mapName', $ucModuleId . 'Map'); + +// Start building things! + +// Make the module directory +$modulePath = 'modules/' . $moduleId; + +if (file_exists($modulePath)) { + error("$modulePath already exists!"); +} else { + mkdir($modulePath) || error("Can't mkdir($modulePath)"); +} + +// Create module.inc +$fd = safe_fopen("$modulePath/module.inc"); +fwrite($fd, $smarty->fetch(__DIR__ . '/module.inc.tpl')); +fclose($fd); + +// Create our sample view and template +$fd = safe_fopen("$modulePath/$ucModuleId.inc"); +fwrite($fd, $smarty->fetch(__DIR__ . '/MyPage.inc.tpl')); +fclose($fd); + +mkdir("$modulePath/templates"); +$fd = safe_fopen("$modulePath/templates/$ucModuleId.tpl"); +fwrite($fd, $smarty->fetch(__DIR__ . '/MyPage.tpl.tpl')); +fclose($fd); + +// Create our map +mkdir($modulePath . '/classes'); +mkdir($modulePath . '/classes/GalleryStorage'); + +$smarty->assign('makefileType', 'classes'); +$fd = safe_fopen("$modulePath/classes/GNUmakefile"); +fwrite($fd, $smarty->fetch(__DIR__ . '/GNUmakefile.tpl')); +fclose($fd); + +$smarty->assign('makefileType', 'GalleryStorage'); +$fd = safe_fopen("$modulePath/classes/GalleryStorage/GNUmakefile"); +fwrite($fd, $smarty->fetch(__DIR__ . '/GNUmakefile.tpl')); +fclose($fd); + +$fd = safe_fopen("$modulePath/classes/Maps.xml"); +fwrite($fd, $smarty->fetch(__DIR__ . '/map.tpl')); +fclose($fd); + +$fd = safe_fopen($modulePath . '/classes/' . $ucModuleId . 'Helper.class'); +fwrite($fd, $smarty->fetch(__DIR__ . '/MyPageHelper.class.tpl')); +fclose($fd); + +echo "* * * * * * * * * * * * * * * * * * * * * * * * * *\n"; +echo "Your module is ready! You must build it by doing: \n"; +echo "\n"; +echo " cd modules/$moduleId/classes \n"; +echo " make && make clean\n"; +echo "\n"; +echo "Then you can go to the Site Admin -> Modules \n"; +echo "page and install and activate your module!\n"; +echo "* * * * * * * * * * * * * * * * * * * * * * * * * *\n"; + +function ask($prompt, $default = '') { + echo $prompt; + + if (!empty($default)) { + echo " [$default]"; + } + echo ' '; + $line = trim(fgets(stdin())); + + if (empty($line)) { + return $default; + } + + return $line; +} + +function error($message) { + fwrite(stderr(), "$message\n"); + fwrite(stderr(), "*** Exiting!\n"); + cleanup(); + + exit(1); +} + +function cleanup() { + global $tmpdir; + + if (file_exists($tmpdir)) { + system("rm -rf $tmpdir"); + } +} + +function safe_fopen($path) { + ($fd = fopen($path, 'wb')) || error("Can't write to $path"); + + return $fd; +} + +function stdin() { + static $stdin; + + if (!defined('STDERR')) { + // Already defined for CLI but not for CGI + $stdin = fopen('php://stdin', 'w'); + define('STDERR', $stdin); + } + + return STDERR; +} + +function stderr() { + static $stderr; + + if (!defined('STDERR')) { + // Already defined for CLI but not for CGI + $stderr = fopen('php://stderr', 'w'); + define('STDERR', $stderr); + } + + return STDERR; +} diff --git a/lib/tools/creator/map.tpl b/lib/tools/creator/map.tpl new file mode 100644 index 00000000..3071eeee --- /dev/null +++ b/lib/tools/creator/map.tpl @@ -0,0 +1,21 @@ + + + + {$mapName} + + 1 + 0 + + + itemId + INTEGER + + + + itemValue + STRING + MEDIUM + + + + diff --git a/lib/tools/creator/module.inc.tpl b/lib/tools/creator/module.inc.tpl new file mode 100644 index 00000000..84dbf265 --- /dev/null +++ b/lib/tools/creator/module.inc.tpl @@ -0,0 +1,62 @@ +setId('{$moduleId}'); + $this->setName($gallery->i18n('{$moduleName}')); + $this->setDescription($gallery->i18n('My {$moduleName} module')); + $this->setVersion('1.0.0'); + $this->_templateVersion = 1; + $this->setCallbacks('getItemLinks'); + $this->setGroup('other', $gallery->i18n('Other')); + $this->setRequiredCoreApi(array(7, 20)); + $this->setRequiredModuleApi(array(3, 6)); + {rdelim} + + /** + * @see GalleryModule::getItemLinks() + */ + function getItemLinks($items, $wantsDetailedLinks, $permissions) {ldelim} + $links = array(); + foreach ($items as $item) {ldelim} + $params['view'] = '{$moduleId}.{$viewName}'; + $params['itemId'] = $item->getId(); + $links[$item->getId()][] = array('text' => $this->translate('{$moduleName}'), 'params' => $params); + {rdelim} + + return array(null, $links); + {rdelim} +{rdelim} +?> diff --git a/lib/tools/dtd/DatabaseChangeDefinition2.0.dtd b/lib/tools/dtd/DatabaseChangeDefinition2.0.dtd new file mode 100644 index 00000000..2a1a602e --- /dev/null +++ b/lib/tools/dtd/DatabaseChangeDefinition2.0.dtd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/tools/dtd/DatabaseRemoveDefinition2.0.dtd b/lib/tools/dtd/DatabaseRemoveDefinition2.0.dtd new file mode 100644 index 00000000..a9658ef7 --- /dev/null +++ b/lib/tools/dtd/DatabaseRemoveDefinition2.0.dtd @@ -0,0 +1,7 @@ + + + + + + + diff --git a/lib/tools/dtd/DatabaseTableDefinition2.0.dtd b/lib/tools/dtd/DatabaseTableDefinition2.0.dtd new file mode 100644 index 00000000..abe51bbf --- /dev/null +++ b/lib/tools/dtd/DatabaseTableDefinition2.0.dtd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/lib/tools/dtd/GalleryClass2.1.dtd b/lib/tools/dtd/GalleryClass2.1.dtd new file mode 100644 index 00000000..6a80dfe6 --- /dev/null +++ b/lib/tools/dtd/GalleryClass2.1.dtd @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/tools/dtd/MapsDefinition2.0.dtd b/lib/tools/dtd/MapsDefinition2.0.dtd new file mode 100644 index 00000000..dd02da27 --- /dev/null +++ b/lib/tools/dtd/MapsDefinition2.0.dtd @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/tools/phpunit/php.ini b/lib/tools/phpunit/php.ini new file mode 100644 index 00000000..aadb6594 --- /dev/null +++ b/lib/tools/phpunit/php.ini @@ -0,0 +1,3 @@ +display_errors=on +allow_url_fopen=Off +include_path=/bogus diff --git a/lib/tools/phpunit/phpinfo.php b/lib/tools/phpunit/phpinfo.php new file mode 100644 index 00000000..fd741b93 --- /dev/null +++ b/lib/tools/phpunit/phpinfo.php @@ -0,0 +1,80 @@ +(.*)#ims', $phpinfo, $matches); +$phpinfo = $matches[1]; +$phpinfo = preg_replace_callback( + '#()(.*?)()#ims', + function ($matches) { + return $matches[1] . wordwrap($matches[2], 10, '', true) . $matches[3]; + }, + $phpinfo +); + +?> + + + + Gallery Support | PHP Info + + + + + +
+
+ Gallery » + Support » PHP Info +
+ +
+ + diff --git a/lib/tools/reports/localization.php b/lib/tools/reports/localization.php new file mode 100644 index 00000000..185282cb --- /dev/null +++ b/lib/tools/reports/localization.php @@ -0,0 +1,374 @@ + $mostRecentPoDate) { + $mostRecentPoDate = $stat['mtime']; + } + + $fuzzy = $translated = $untranslated = $obsolete = 0; + /* + * Untranslated: + * msgid "foo" + * msgstr "" + * + * Translated: + * msgid "foo" + * msgstr "bar" + * + * Translated: + * msgid "foo" + * msgstr "" + * "blah blah blah" + * + * Untranslated: + * msgid "foo" + * msgid_plural "foos" + * msgstr[0] "" + * msgstr[1] "" + * msgstr[2] "" + * + * Translated: + * msgid "foo" + * msgid_plural "foos" + * msgstr[0] "bar1" + * msgstr[1] "bar2" + * msgstr[2] "bar3" + * + * Translated, Fuzzy: + * # fuzzy + * msgid "foo" + * msgstr "bar" + * + * Deleted, Fuzzy: + * # fuzzy + * #~ msgid "foo" + * #~ msgstr "bar" + * + */ + $msgId = null; + $nextIsFuzzy = $lastLineWasEmptyMsgStr = $lastLineWasEmptyMsgId = 0; + + foreach (file($poFile) as $line) { + /* + * Scan for: + * msgid "foo bar" + * + * and: + * msgid "" + * "foo bar" + */ + if (preg_match('/^msgid "(.*)"/', $line, $matches)) { + if (empty($matches[1])) { + $lastLineWasEmptyMsgId = 1; + } else { + $msgId = $line; + } + + continue; + } + + /* + * Scan for: + * msgid "" + * "foo bar" + */ + if ($lastLineWasEmptyMsgId) { + if (preg_match('/^\s*"(.*)"/', $line, $matches)) { + $msgId = $line; + } + $lastLineWasEmptyMsgId = 0; + + continue; + } + + if (strpos($line, '#, fuzzy') === 0) { + $nextIsFuzzy = 1; + + continue; + } + + if (preg_match('/^#~ msgid "(.*)"/', $line, $matches)) { + $obsolete++; + $nextIsFuzzy = 0; + } + + /* + * Scan for: + * msgstr "" + * "foo bar" + */ + if ($lastLineWasEmptyMsgStr) { + if (preg_match('/^\s*".+"/', $line)) { + if ($nextIsFuzzy) { + $fuzzy++; + } + $translated++; + } else { + if ($nextIsFuzzy) { + echo "ERROR: DISCARD FUZZY for [$locale, $plugin, $msgId]
"; + } + $untranslated++; + } + $msgId = null; + $nextIsFuzzy = 0; + $lastLineWasEmptyMsgStr = 0; + } + + /* + * Scan for: + * msgstr "foo bar" + * + * or: + * msgstr "" + * "foo bar" + */ + if (!empty($msgId)) { + if (preg_match('/^msgstr/', $line)) { + if (preg_match('/^msgstr[\d\[\]]*\s*""\s*$/', $line)) { + $lastLineWasEmptyMsgStr = 1; + } else { + if ($nextIsFuzzy) { + $fuzzy++; + } + $translated++; + $msgId = null; + $nextIsFuzzy = 0; + } + } + } + } + // Catch msgstr "" in last line + if (!empty($msgId) && $lastLineWasEmptyMsgStr) { + $untranslated++; + } + + $total = $translated + $untranslated; + + if (empty($total)) { + $percentDone = $exactPercentDone = 0; + } else { + $percentDone = floor(($translated - $fuzzy) * 100 * $pow / $total) / $pow; + $exactPercentDone = ($translated - $fuzzy) * 100 / $total; + } + $poData[$locale]['plugins'][$plugin] = array( + 'translated' => $translated, + 'untranslated' => $untranslated, + 'total' => $total, + 'fuzzy' => $fuzzy, + 'obsolete' => $obsolete, + 'percentDone' => $percentDone, + 'exactPercentDone' => $exactPercentDone, + 'name' => $plugin, + ); + $totalTranslated += $translated - $fuzzy; + + foreach (array('translated', 'untranslated', 'fuzzy', 'obsolete') as $key) { + if (!isset($summary[$locale][$key])) { + $summary[$locale][$key] = 0; + } + + $summary[$locale][$key] += $poData[$locale]['plugins'][$plugin][$key]; + } + + // Keep track of the largest message count we've seen per plugin + if (empty($maxMessageCount[$plugin]) || $total > $maxMessageCount[$plugin]) { + $maxMessageCount[$plugin] = $total; + } + } + + // Overall total message count + $overallTotal = array_sum(array_values($maxMessageCount)); + + foreach (array_keys($poData) as $locale) { + $pluginTotal = 0; + + // Fill in any missing locales + foreach (array_keys($seenPlugins) as $plugin) { + if (!isset($poData[$locale]['plugins'][$plugin])) { + $poData[$locale]['plugins'][$plugin]['missing'] = 1; + $poData[$locale]['plugins'][$plugin]['percentDone'] = 0; + $poData[$locale]['plugins'][$plugin]['exactPercentDone'] = 0; + $poData[$locale]['plugins'][$plugin]['name'] = $plugin; + } else { + $pluginTotal += $poData[$locale]['plugins'][$plugin]['translated'] - $poData[$locale]['plugins'][$plugin]['fuzzy']; + } + } + uasort($poData[$locale]['plugins'], 'sortByPercentDone'); + + // Figure out total percentage + if (empty($overallTotal)) { + $poData[$locale]['percentDone'] = $poData[$locale]['exactPercentDone'] = 0; + } else { + $poData[$locale]['percentDone'] = floor($pluginTotal * 100 * $pow / $overallTotal) / $pow; + $poData[$locale]['exactPercentDone'] = $pluginTotal * 100 / $overallTotal; + } + + foreach (array('translated', 'untranslated', 'fuzzy', 'obsolete') as $key) { + $poData[$locale]['summary'][$key] = floor($summary[$locale][$key] * 100 * $pow / $overallTotal) / $pow; + } + $poData[$locale]['summary']['total'] = $overallTotal; + } + + // Sort locales by overall total + uasort($poData, 'sortByPercentDone'); + + return array($poData, $mostRecentPoDate, $totalTranslated); +} + +/** + * Comparison function to be called by uasort elsewhere. The given params are arrays expected to + * have at least a key of 'percentDone' with a numeric value. Optionally the arrays may have + * two more keys: 'missing' and 'name'. Main sort is by 'percentDone' DESC. But if one of the + * given params has a key 'missing' and the other not, the entry with 'missing' is sorted last, + * regardless of percentage. When two entries are otherwise equal and both have a 'name' entry, + * then the sort is by 'name' ASC. + * + * @param array $a first entry to sort + * @param array $b second entry to sort + * @return int -1, 0, +1, depending on the comparision + */ +function sortByPercentDone($a, $b) { + if (isset($a['missing']) && !isset($b['missing'])) { + return 1; + } + + if (isset($b['missing']) && !isset($a['missing'])) { + return -1; + } + + if ($a['exactPercentDone'] == $b['exactPercentDone']) { + if (isset($a['name'], $b['name'])) { + return ($a['name'] < $b['name']) ? -1 : 1; + } + + return 0; + } + + return ($a['exactPercentDone'] < $b['exactPercentDone']) ? 1 : -1; +} + +function percentColor($percent) { + $border = 50; + + if ($percent < $border) { + $color = dechex(255 - $percent * 2) . '0000'; + } else { + $color = '00' . dechex(55 + $percent * 2) . '00'; + } + + if (strlen($color) < 6) { + $color = '0' . $color; + } + + return $color; +} + +function newRow() { + $count =& getRowCount(); + $count++; +} + +function &getRowCount() { + static $count; + + if (!isset($count)) { + $count = 0; + } + + return $count; +} + +function modifier($string) { + $count =& getRowCount(); + + if ($count % 2) { + return $string . '_light'; + } + + return $string . '_dark'; +} diff --git a/lib/tools/reports/localization/main_detail.inc b/lib/tools/reports/localization/main_detail.inc new file mode 100644 index 00000000..a7cce09d --- /dev/null +++ b/lib/tools/reports/localization/main_detail.inc @@ -0,0 +1,56 @@ + + + + Gallery 2: Localization Status Report (<?php echo strftime('%x'); ?>) + + + +

Localization Status Report for Gallery 2

+

Generated:
+ Most Recent PO:
+ Total Translated Strings:

+ +
+ View: summary | detail + + + $localeData) { + ?> + + + + + + + + + + + + + + + + + + + + + +
+ + + + + % +
+
+ + diff --git a/lib/tools/reports/localization/main_summary.inc b/lib/tools/reports/localization/main_summary.inc new file mode 100644 index 00000000..482613bb --- /dev/null +++ b/lib/tools/reports/localization/main_summary.inc @@ -0,0 +1,68 @@ + + + + Gallery 2: Localization Status Report (<?php echo strftime('%x'); ?>) + + + +

Localization Status Report for Gallery 2

+

Generated:
+ Most Recent PO:
+ Total Translated Strings:

+ +
+ View: summary | detail + + + $localeData) { + ?> + + + + + + + + + + + + + + + + +
+ + + + + % + + + + % + + % + + ' + . (100 - $summaryData['translated']) + . '% (' + . $summaryData['untranslated'] . '%)'; ?> + + % +
+
+ + diff --git a/lib/tools/reports/localization/plugin_rowdata.inc b/lib/tools/reports/localization/plugin_rowdata.inc new file mode 100644 index 00000000..22ea0a5b --- /dev/null +++ b/lib/tools/reports/localization/plugin_rowdata.inc @@ -0,0 +1,34 @@ + + + + + + % + + + + + Missing + + + + + + + + + + + + + + + + + + diff --git a/lib/tools/reports/localization/row_header_detail.inc b/lib/tools/reports/localization/row_header_detail.inc new file mode 100644 index 00000000..ac4b2d03 --- /dev/null +++ b/lib/tools/reports/localization/row_header_detail.inc @@ -0,0 +1,32 @@ + + + Rank + + + Locale + + + Complete + + + Plugin + + + Complete + + + Total + + + Trans. + + + Fuzzy + + + Untrans. + + + Obsolete + + diff --git a/lib/tools/reports/localization/row_header_summary.inc b/lib/tools/reports/localization/row_header_summary.inc new file mode 100644 index 00000000..ee7d58cc --- /dev/null +++ b/lib/tools/reports/localization/row_header_summary.inc @@ -0,0 +1,26 @@ + + + Rank + + + Locale + + + Complete + + + Total + + + Trans. + + + Fuzzy + + + Untrans. + + + Obsolete + + diff --git a/lib/tools/reports/localization/style.css b/lib/tools/reports/localization/style.css new file mode 100644 index 00000000..faadfeab --- /dev/null +++ b/lib/tools/reports/localization/style.css @@ -0,0 +1,58 @@ +h1 { + font-size: 150%; + text-align: center; +} + +h3 { + font-size: 110%; + text-align: center; +} + +BODY { + font-family: verdana, sans-serif; + font-style: normal; + font-variant: normal; + font-weight: normal; + text-decoration: none; + letter-spacing: normal; + margin: 0; + padding: 0; + background-color : #eeeeee; + color : #000000; +} + +td, th { border: 1px solid black; } + +th { + background-color: #5555FF; + color: #FFFF55; + font-size: 85%; + font-variant: small-caps; +} + +table, tr, td, p { font-size: 90%; } +P, address { margin: 0 20px 5px 20px; } + +.report { width: 100%; } +.translated_light { background-color: #E1E9FF; } +.fuzzy_light { background-color: #FFFF00; } +.untranslated_light { background-color: #FF8000; } +.obsolete_light { background-color: #FF80FF; } + + +.locale_percent { } +.translated_dark { background-color: #C5D5FF; } +.fuzzy_dark { background-color: #D0D000; } +.untranslated_dark { background-color: #E07000; } +.obsolete_dark { background-color: #E000E0; } +.missing_light { background-color: #FFCC99; } +.missing_dark { background-color: #FF9999; } +.total_light { background-color: #BBBBBB; } +.total_dark { background-color: #999999; } + +.light { background-color:#FFFFFF; } +.dark { background-color:#CECECE; } + +td { padding : 5px; } + +.col3_right { text-align: right } diff --git a/lib/tools/stubs/.htaccess b/lib/tools/stubs/.htaccess new file mode 100644 index 00000000..a43452d8 --- /dev/null +++ b/lib/tools/stubs/.htaccess @@ -0,0 +1,3 @@ +Order deny,allow +Deny from all +Allow from none diff --git a/lib/tools/stubs/README b/lib/tools/stubs/README new file mode 100644 index 00000000..ddec252d --- /dev/null +++ b/lib/tools/stubs/README @@ -0,0 +1,6 @@ +These are stubs of classes provided by external toolkits, gathered +together in one place to allow phpDoc to create references to the +classes without having to run phpDoc on the toolkits themselves (which +are generally not phpDoc compliant). They aren't actually used +anywhere in the production code. + diff --git a/lib/tools/stubs/Smarty_stub.class b/lib/tools/stubs/Smarty_stub.class new file mode 100644 index 00000000..ffa5ce11 --- /dev/null +++ b/lib/tools/stubs/Smarty_stub.class @@ -0,0 +1,28 @@ + + * @version $Revision: 17580 $ + */ +class Smarty { +} diff --git a/lib/tools/stubs/TestCase_stub.class b/lib/tools/stubs/TestCase_stub.class new file mode 100644 index 00000000..7526fcc1 --- /dev/null +++ b/lib/tools/stubs/TestCase_stub.class @@ -0,0 +1,31 @@ + + * @version $Revision: 17580 $ + */ +class TestCase { +} + +class TestResult { +} diff --git a/lib/tools/uml/GNUmakefile b/lib/tools/uml/GNUmakefile new file mode 100644 index 00000000..a54a2109 --- /dev/null +++ b/lib/tools/uml/GNUmakefile @@ -0,0 +1,8 @@ +all: + perl make-java-classes.pl + +clean: + rm -rf tmp + +compile: + (cd tmp && javac `find . -name '*.java'`) diff --git a/lib/tools/uml/JavaClasses.xsl b/lib/tools/uml/JavaClasses.xsl new file mode 100644 index 00000000..f163b2af --- /dev/null +++ b/lib/tools/uml/JavaClasses.xsl @@ -0,0 +1,51 @@ + + + + + + +/* + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2008 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * Java peer class for the class. + * (Class schema version: .) + * + * This file is automatically generated from XML embedded in the + * class, combined with the JavaClasses.xsl file. You should not modify + * it by hand, as your changes will be lost next time this file is + * rebuilt. If you want to add more getters/setters, you should add + * them in the core class. If you want to change the format of this + * file, then you should edit the XSL template. + */ +public class + extends { + + private String ; + + + + public String get() { return ; } + public void set(String value) { = value; } + + +} + + diff --git a/lib/tools/uml/make-java-classes.pl b/lib/tools/uml/make-java-classes.pl new file mode 100644 index 00000000..6d435e2e --- /dev/null +++ b/lib/tools/uml/make-java-classes.pl @@ -0,0 +1,106 @@ +#!/usr/local/bin/perl +use strict; +use File::Basename; + +my $GALLERYDIR = "../../.."; +my $TOOLDIR = ".."; +my $TMPDIR = "tmp"; +my $OUTPUTDIR = "tmp"; +my $SAXON_JAR = $ENV{'SAXON_JAR'} ? $ENV{'SAXON_JAR'} : "/usr/local/share/java/classes/saxon.jar"; +my $SAXON = "java -jar $SAXON_JAR"; +my $XSLFILE = "JavaClasses.xsl"; + +foreach my $module (<$GALLERYDIR/modules/*>) { + &generate(basename($module)); +} + +sub generate { + my $module = shift; + mkdir($TMPDIR) unless -d $TMPDIR; + mkdir("$TMPDIR/$module") unless -d "$TMPDIR/$module"; + + foreach my $classFile (<$GALLERYDIR/modules/$module/classes/*.class>) { + (my $base = basename($classFile)) =~ s/\..*?$//; + my $xmlFile = "$TMPDIR/$module/$base.xml"; + + if (! -f $xmlFile || ((stat($classFile))[9] > (stat($xmlFile))[9])) { + system("perl $TOOLDIR/bin/extractClassXml.pl " . + "--dtd=../../../dtd/GalleryClass2.0.dtd " . + "--stub-ok " . + "--out $xmlFile $classFile " . + "--quiet"); + if (-z $xmlFile) { + unlink($xmlFile); + } + } + + if (-f $xmlFile) { + my $javaFile = "$TMPDIR/$module/$base.java"; + if (! -f $javaFile || ((stat($xmlFile))[9] > (stat($javaFile))[9])) { + system("$SAXON $xmlFile $XSLFILE | perl -pe 's/\@\@package\@\@/$module/' > $javaFile"); + + open(FD, "<$javaFile") || die; + chomp(my @lines = ); + close(FD); + + unshift(@lines, "package $module;"); + for (my $i = 0; $i < @lines; $i++) { + $lines[$i] =~ s/extends GalleryPersistent//; + + # HACK: make all non module classes "extends" + # clauses extend from core.Xxx + if ($module ne "core") { + $lines[$i] =~ s/extends (\S+)/extends core.$1/; + } + } + + open(FD, ">$javaFile") || die; + print FD join("\n", @lines); + close(FD); + } + } + } +} + +__END__ + + +TOOLDIR ?= $(GALLERYDIR)/lib/tools +XSLFILE ?= $(TOOLDIR)/uml/JavaClasses.xsl +G2_TMPDIR ?= tmp + +include $(TOOLDIR)/GNUmakefile.inc + +CLASSFILES = $(wildcard $(CLASSDIR)/*.class) +XMLFILES = $(patsubst $(CLASSDIR)/%.class,%.xml,$(CLASSFILES)) +JAVAFILES = $(patsubst %.xml,%.java,$(XMLFILES)) + +java: $(G2_TMPDIR) $(JAVAFILES) + +$(G2_TMPDIR): + mkdir $(G2_TMPDIR) + +%.java: $(G2_TMPDIR)/%.xml $(XSLFILE) + if [ -f $< ]; then $(SAXON) $< $(XSLFILE) \ + | perl -pe 's/\@\@package\@\@/$(PACKAGE)/' \ + > $@; fi + +$(G2_TMPDIR)/%.xml: $(CLASSDIR)/%.class + perl $(TOOLDIR)/bin/extractClassXml.pl \ + --dtd=../$(TOOLDIR)/dtd/GalleryClass2.0.dtd \ + --stub-ok \ + --out $@ \ + $^ + if [ -f $@ ]; then $(VALIDATOR) $@; fi + +clean: + rm -rf $(G2_TMPDIR) + +scrub: clean + rm -f *.java + +# Gmake will automatically delete $(G2_TMPDIR)/*.xml files after creating .java files +# because it thinks that they're intermediate files. But, we want to save +# them (for now), so mark them as PRECIOUS. +# +.PRECIOUS: $(G2_TMPDIR)/%.xml diff --git a/modules/addtoany/po/ca.po b/modules/addtoany/po/ca.po index 64cde2a6..f1f45cd3 100644 --- a/modules/addtoany/po/ca.po +++ b/modules/addtoany/po/ca.po @@ -1,4 +1,4 @@ -# $Id: header.txt 17580 2008-04-13 00:38:13Z tnalmdal $ +# $Id$ # # Gallery - a web based photo album viewer and editor # Copyright (C) 2000-2008 Bharat Mediratta @@ -16,19 +16,8 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. -# msgid "" -msgstr "" -"Project-Id-Version: Gallery: AddToAny 1.0.1\n" -"Report-Msgid-Bugs-To: gallery-translations@lists.sourceforge.net\n" -"POT-Creation-Date: 2009-11-29 00:24+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" +msgstr "Project-Id-Version: Gallery: AddToAny 1.0.1\nReport-Msgid-Bugs-To: gallery-translations@lists.sourceforge.net\nPOT-Creation-Date: 2009-11-29 00:24+0100\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\nLast-Translator: FULL NAME \nLanguage-Team: LANGUAGE \nLanguage: \nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n" msgid "AddToAny" msgstr "" @@ -54,8 +43,7 @@ msgstr "" msgid "General settings" msgstr "Configuración general" -msgid "" -"These control the settings which apply globally to the AddToAny link blocks on your Gallery" +msgid "These control the settings which apply globally to the AddToAny link blocks on your Gallery" msgstr "" msgid "Show AddToAny links only when logged in (hide for guests)" diff --git a/modules/addtoany/po/es.po b/modules/addtoany/po/es.po index d37e9155..d9ca42d6 100644 --- a/modules/addtoany/po/es.po +++ b/modules/addtoany/po/es.po @@ -1,4 +1,4 @@ -# $Id: header.txt 17580 2008-04-13 00:38:13Z tnalmdal $ +# $Id$ # # Gallery - a web based photo album viewer and editor # Copyright (C) 2000-2008 Bharat Mediratta @@ -16,19 +16,8 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. -# msgid "" -msgstr "" -"Project-Id-Version: Gallery: AddToAny 1.0.1\n" -"Report-Msgid-Bugs-To: gallery-translations@lists.sourceforge.net\n" -"POT-Creation-Date: 2019-02-03 22:39+0100\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" +msgstr "Project-Id-Version: Gallery: AddToAny 1.0.1\nReport-Msgid-Bugs-To: gallery-translations@lists.sourceforge.net\nPOT-Creation-Date: 2019-02-03 22:39+0100\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\nLast-Translator: FULL NAME \nLanguage-Team: LANGUAGE \nLanguage: \nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\n" msgid "AddToAny" msgstr "" @@ -54,8 +43,7 @@ msgstr "Se ha producido un error" msgid "General settings" msgstr "Configuración general" -msgid "" -"These control the settings which apply globally to the AddToAny link blocks on your Gallery" +msgid "These control the settings which apply globally to the AddToAny link blocks on your Gallery" msgstr "Desde aquí puedes controlar la configuración de compartición para tu Gallery" msgid "Show AddToAny links only when logged in (hide for guests)" diff --git a/modules/addtoany/po/messages.po b/modules/addtoany/po/messages.po index 32d0bafb..aa53133d 100644 --- a/modules/addtoany/po/messages.po +++ b/modules/addtoany/po/messages.po @@ -17,6 +17,48 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. # +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or (at +# your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. +# #, fuzzy msgid "" msgstr "" diff --git a/modules/core/CoreModuleExtras.inc b/modules/core/CoreModuleExtras.inc index c98e9812..af8619b6 100644 --- a/modules/core/CoreModuleExtras.inc +++ b/modules/core/CoreModuleExtras.inc @@ -3319,7 +3319,11 @@ if (!isset($gallery) || !method_exists($gallery, \'setConfig\')) { GalleryCoreApi::requireOnce('modules/core/classes/GalleryEntity.class'); $lock = new GalleryEntity(); - $lock->create(); + $ret = $lock->create(); + if ($ret) { + return $ret; + } + $ret = $lock->save(false); if ($ret) { return $ret; diff --git a/modules/mapv3/classes/GoogleMapUtilities.class b/modules/mapv3/classes/GoogleMapUtilities.class index 7abf5b28..429269b6 100644 --- a/modules/mapv3/classes/GoogleMapUtilities.class +++ b/modules/mapv3/classes/GoogleMapUtilities.class @@ -557,7 +557,7 @@ class GoogleMapUtilities { $NorthSouth = ''; $EastWest = ''; - if (is_array($temp[0])) { + if (is_array($temp) && is_array($temp[0])) { foreach ($temp[0] as $id => $data) { if (is_array($data) and array_key_exists('Tag Name', $temp[0][$id]) and ($temp[0][$id]['Tag Name'] == 'GPS Info Image File Directory (IFD)') diff --git a/modules/rewrite/test/phpunit/IsapiRewriteHelperTest.class b/modules/rewrite/test/phpunit/IsapiRewriteHelperTest.class index 146ee7f2..8f54b160 100644 --- a/modules/rewrite/test/phpunit/IsapiRewriteHelperTest.class +++ b/modules/rewrite/test/phpunit/IsapiRewriteHelperTest.class @@ -229,7 +229,7 @@ class IsapiRewriteHelperMockPlatform extends GalleryPlatform { // The hostName can include the port part list($host) = explode(':', $urlGenerator->getHostName()); - if ($target == $host) { + if (str_contains($target, $host)) { return 'phpunit'; } diff --git a/modules/rewrite/test/phpunit/ModRewriteHelperTest.class b/modules/rewrite/test/phpunit/ModRewriteHelperTest.class index e0551b2a..4df1f801 100644 --- a/modules/rewrite/test/phpunit/ModRewriteHelperTest.class +++ b/modules/rewrite/test/phpunit/ModRewriteHelperTest.class @@ -374,7 +374,7 @@ class ModRewriteHelperMockPlatform extends GalleryPlatform { } public function fsockopen($target, $port, &$errno, &$errstr, $timeout) { - if ($target == 'www.test.server') { + if (str_contains($target, 'www.test.server')) { return 'phpunit'; } diff --git a/modules/rewrite/test/phpunit/PathInfoHelperTest.class b/modules/rewrite/test/phpunit/PathInfoHelperTest.class index 2e630b83..6eb6bfd7 100644 --- a/modules/rewrite/test/phpunit/PathInfoHelperTest.class +++ b/modules/rewrite/test/phpunit/PathInfoHelperTest.class @@ -125,7 +125,7 @@ class PathInfoHelperMockPlatform extends GalleryPlatform { // The hostName can include the port part list($host) = explode(':', $urlGenerator->getHostName()); - if ($target == $host) { + if (str_contains($target, $host)) { return 'phpunit'; } diff --git a/modules/rewrite/test/phpunit/RewriteModuleTest.class b/modules/rewrite/test/phpunit/RewriteModuleTest.class index 1ff606c1..590d1425 100644 --- a/modules/rewrite/test/phpunit/RewriteModuleTest.class +++ b/modules/rewrite/test/phpunit/RewriteModuleTest.class @@ -201,7 +201,7 @@ class RewriteModuleTest extends GalleryTestCase { } $this->assertEquals( 'phpunit/dummy/%itemId%.html', - unserialize($history), + unserialize($history??''), 'saved history' ); diff --git a/modules/rewrite/test/phpunit/RewriteParserTestCase.class b/modules/rewrite/test/phpunit/RewriteParserTestCase.class index cf20d331..37d4a558 100644 --- a/modules/rewrite/test/phpunit/RewriteParserTestCase.class +++ b/modules/rewrite/test/phpunit/RewriteParserTestCase.class @@ -599,7 +599,7 @@ class RewriteParserMockPlatform extends GalleryPlatform { // The hostName can include the port part list($host) = explode(':', $urlGenerator->getHostName()); - if ($target == $host) { + if (str_contains($target, $host)) { return 'phpunit'; } diff --git a/modules/watermark/module.inc b/modules/watermark/module.inc index 8514b601..3e224811 100644 --- a/modules/watermark/module.inc +++ b/modules/watermark/module.inc @@ -185,6 +185,8 @@ class WatermarkModule extends GalleryModule /* and GalleryEventListener */ { case '1.1.5': case '1.1.6': case '1.1.7': + case '1.1.8': + case '1.2.0': case 'end of upgrade path': /* @@ -271,7 +273,7 @@ class WatermarkModule extends GalleryModule /* and GalleryEventListener */ { } $ret = GalleryCoreApi::registerFactoryImplementation( - 'GalleryEventListener', 'WatermarkModule', 'WatermarkModule', + 'GalleryEventListener', 'WatermarkModule', 'WatermarkModule', 'modules/watermark/module.inc', 'watermark', array('GalleryEntity::delete')); if ($ret) { return $ret; diff --git a/php.ini.sample b/php.ini.sample new file mode 100644 index 00000000..1d85dab5 --- /dev/null +++ b/php.ini.sample @@ -0,0 +1,8 @@ +memory_limit=256M +output_buffer=0 +post_max_size=64M +upload_max_filesize=64M +error_log=php-error.log +log_errors=0 +allow_url_fopen=Off +display_errors=0 diff --git a/upgrade/php.ini b/upgrade/php.ini new file mode 100644 index 00000000..d6ff00af --- /dev/null +++ b/upgrade/php.ini @@ -0,0 +1,26 @@ +;; memory_limit = 82M; +memory_limit=256M +upload_max_filesize = 128M +post_max_size=128M + + +;; max_execution_time = 5000; +;;url_rewriter.tags = "a=href,area=href,frame=src,form=fakeentry,fieldset="; + +;; +allow_call_time_pass_reference = Off +allow_url_fopen = On +display_errors = Off +register_globals = Off +short_open_tag = Off +;;magic_quotes_gpc = Off + +;; Debug +error_reporting = E_ALL & ~E_DEPRECATED + +output_buffering=Off + +;;log_errors = On +display_errors = Off +;;error_log = php-error.log +;;display_startup_errors=on