diff --git a/modules/adsense/controllers/admin_adsense.php b/modules/adsense/controllers/admin_adsense.php new file mode 100644 index 00000000..13be83d8 --- /dev/null +++ b/modules/adsense/controllers/admin_adsense.php @@ -0,0 +1,57 @@ +page_title = t("Adsense settings"); + $view->content = new View("admin_adsense.html"); + $view->content->form = $this->_get_admin_form(); + print $view; + } + + public function save() { + access::verify_csrf(); + $form = $this->_get_admin_form(); + if ($form->validate()) { + module::set_var("adsense", "code", $form->adsense->code->value); + module::set_var("adsense", "location", $form->adsense->location->value); + message::success(t("Adsense settings updated")); + url::redirect("admin/adsense"); + } else { + print $form; + } + } + + private function _get_admin_form() { + $form = new Forge("admin/adsense/save", "", "post", array("id" => "g-adsense-admin-form")); + $adsense_settings = $form->group("adsense")->label(t("Adsense settings")); + $adsense_settings->textarea("code")->label(t("Adsense code")) + ->value(module::get_var("adsense", "code")); + $adsense_settings->dropdown("location") + ->label(t("Where should the ads be displayed?")) + ->options(array("header" => t("In the header"), + "sidebar" => t("In the sidebar"), + "footer" => t("In the footer"))) + ->selected(module::get_var("adsense", "location")); + $adsense_settings->submit("save")->value(t("Save")); + return $form; + } +} + diff --git a/modules/adsense/helpers/adsense_block.php b/modules/adsense/helpers/adsense_block.php new file mode 100644 index 00000000..7c00cb45 --- /dev/null +++ b/modules/adsense/helpers/adsense_block.php @@ -0,0 +1,39 @@ + t("Adsense")); + } + + static function get($block_id, $theme) { + $block = ""; + switch ($block_id) { + case "adsense": + if (module::get_var("adsense", "location") == "sidebar") { + $block = new Block(); + $block->css_id = "g-adsense"; + $block->title = t("Adsense"); + $block->content = new View("adsense_block.html"); + } + break; + } + return $block; + } +} \ No newline at end of file diff --git a/modules/adsense/helpers/adsense_event.php b/modules/adsense/helpers/adsense_event.php new file mode 100644 index 00000000..6611ebb1 --- /dev/null +++ b/modules/adsense/helpers/adsense_event.php @@ -0,0 +1,28 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("adsense_menu") + ->label(t("Adsense")) + ->url(url::site("admin/adsense"))); + } +} diff --git a/modules/adsense/helpers/adsense_theme.php b/modules/adsense/helpers/adsense_theme.php new file mode 100644 index 00000000..97293992 --- /dev/null +++ b/modules/adsense/helpers/adsense_theme.php @@ -0,0 +1,54 @@ +' . $code . ' + '; + + return $google_code; + } + } + + static function footer($theme) { + if(module::get_var("adsense","location") == "footer") { + $code = module::get_var("adsense", "code"); + if (!$code) { + return; + } + $google_code = ' + + '; + + return $google_code; + } + } +} diff --git a/modules/adsense/module.info b/modules/adsense/module.info new file mode 100644 index 00000000..41cc63b2 --- /dev/null +++ b/modules/adsense/module.info @@ -0,0 +1,3 @@ +name = "Adsense" +description = "Display Google Adsense ads" +version = 1 diff --git a/modules/adsense/views/admin_adsense.html.php b/modules/adsense/views/admin_adsense.html.php new file mode 100644 index 00000000..f994fae5 --- /dev/null +++ b/modules/adsense/views/admin_adsense.html.php @@ -0,0 +1,7 @@ + +
+

+
+ +
+
diff --git a/modules/adsense/views/adsense_block.html.php b/modules/adsense/views/adsense_block.html.php new file mode 100644 index 00000000..53f8b20a --- /dev/null +++ b/modules/adsense/views/adsense_block.html.php @@ -0,0 +1,17 @@ +' . $code . ' + '; + + echo $google_code; +} +?> + diff --git a/modules/autorotate/helpers/autorotate.php b/modules/autorotate/helpers/autorotate.php index ddf74b78..5bfe1afe 100644 --- a/modules/autorotate/helpers/autorotate.php +++ b/modules/autorotate/helpers/autorotate.php @@ -19,6 +19,11 @@ */ class autorotate { static function rotate_item($item) { + + require_once(MODPATH . 'autorotate/lib/pel/PelDataWindow.php'); + require_once(MODPATH . 'autorotate/lib/pel/PelJpeg.php'); + require_once(MODPATH . 'autorotate/lib/pel/PelTiff.php'); + // Only try to rotate photos based on EXIF if ($item->is_photo() && $item->mime_type == "image/jpeg") { require_once(MODPATH . "exif/lib/exif.php"); @@ -38,9 +43,23 @@ class autorotate { if($degrees) { $tmpfile = tempnam(TMPPATH, "rotate"); gallery_graphics::rotate($item->file_path(), $tmpfile, array("degrees" => $degrees)); + // Update EXIF info + $data = new PelDataWindow(file_get_contents($tmpfile)); + if (PelJpeg::isValid($data)) { + $jpeg = $file = new PelJpeg(); + $jpeg->load($data); + $exif = $jpeg->getExif(); + if($exif !== null) { + $tiff = $exif->getTiff(); + $ifd0 = $tiff->getIfd(); + $orientation = $ifd0->getEntry(PelTag::ORIENTATION); + $orientation->setValue(1); + file_put_contents($tmpfile, $file->getBytes()); + } + } $item->set_data_file($tmpfile); $item->save(); - unlink($tmpfile); + unlink($tmpfile); } } } diff --git a/modules/autorotate/lib/pel/Pel.php b/modules/autorotate/lib/pel/Pel.php new file mode 100644 index 00000000..c1eb8264 --- /dev/null +++ b/modules/autorotate/lib/pel/Pel.php @@ -0,0 +1,337 @@ + + * @version $Revision: 463 $ + * @date $Date: 2006-11-18 23:25:24 +0100 (Sat, 18 Nov 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + + +/* Initialize Gettext, if available. This must be done before any + * part of PEL calls Pel::tra() or Pel::fmt() --- this is ensured if + * every piece of code using those two functions require() this file. + * + * If Gettext is not available, wrapper functions will be created, + * allowing PEL to function, but without any translations. + * + * The PEL translations are stored in './locale'. It is important to + * use an absolute path here because the lookups will be relative to + * the current directory. */ + +if (function_exists('dgettext')) { + bindtextdomain('pel', dirname(__FILE__) . '/locale'); +} else { + + /** + * Pretend to lookup a message in a specific domain. + * + * This is just a stub which will return the original message + * untranslated. The function will only be defined if the Gettext + * extension has not already defined it. + * + * @param string $domain the domain. + * + * @param string $str the message to be translated. + * + * @return string the original, untranslated message. + */ + function dgettext($domain, $str) { + return $str; + } +} + + +/** + * Class with miscellaneous static methods. + * + * This class will contain various methods that govern the overall + * behavior of PEL. + * + * Debugging output from PEL can be turned on and off by assigning + * true or false to {@link Pel::$debug}. + * + * @author Martin Geisler + * @package PEL + */ +class Pel { + + /** + * Flag for controlling debug information. + * + * The methods producing debug information ({@link debug()} and + * {@link warning()}) will only output something if this variable is + * set to true. + * + * @var boolean + */ + private static $debug = false; + + /** + * Flag for strictness of parsing. + * + * If this variable is set to true, then most errors while loading + * images will result in exceptions being thrown. Otherwise a + * warning will be emitted (using {@link Pel::warning}) and the + * exceptions will be appended to {@link Pel::$exceptions}. + * + * Some errors will still be fatal and result in thrown exceptions, + * but an effort will be made to skip over as much garbage as + * possible. + * + * @var boolean + */ + private static $strict = false; + + /** + * Stored exceptions. + * + * When {@link Pel::$strict} is set to false exceptions will be + * accumulated here instead of being thrown. + */ + private static $exceptions = array(); + + + /** + * Return list of stored exceptions. + * + * When PEL is parsing in non-strict mode, it will store most + * exceptions instead of throwing them. Use this method to get hold + * of them when a call returns. + * + * Code for using this could look like this: + * + * + * Pel::setStrictParsing(true); + * Pel::clearExceptions(); + * + * $jpeg = new PelJpeg($file); + * + * // Check for exceptions. + * foreach (Pel::getExceptions() as $e) { + * printf("Exception: %s\n", $e->getMessage()); + * if ($e instanceof PelEntryException) { + * // Warn about entries that couldn't be loaded. + * printf("Warning: Problem with %s.\n", + * PelTag::getName($e->getType(), $e->getTag())); + * } + * } + * + * + * This gives applications total control over the amount of error + * messages shown and (hopefully) provides the necessary information + * for proper error recovery. + * + * @return array the exceptions. + */ + static function getExceptions() { + return self::$exceptions; + } + + + /** + * Clear list of stored exceptions. + * + * Use this function before a call to some method if you intend to + * check for exceptions afterwards. + */ + static function clearExceptions() { + self::$exceptions = array(); + } + + + /** + * Conditionally throw an exception. + * + * This method will throw the passed exception when strict parsing + * in effect (see {@link setStrictParsing()}). Otherwise the + * exception is stored (it can be accessed with {@link + * getExceptions()}) and a warning is issued (with {@link + * Pel::warning}). + * + * @param PelException $e the exceptions. + */ + static function maybeThrow(PelException $e) { + if (self::$strict) { + throw $e; + } else { + self::$exceptions[] = $e; + self::warning('%s (%s:%s)', $e->getMessage(), + basename($e->getFile()), $e->getLine()); + } + } + + + /** + * Enable/disable strict parsing. + * + * If strict parsing is enabled, then most errors while loading + * images will result in exceptions being thrown. Otherwise a + * warning will be emitted (using {@link Pel::warning}) and the + * exceptions will be stored for later use via {@link + * getExceptions()}. + * + * Some errors will still be fatal and result in thrown exceptions, + * but an effort will be made to skip over as much garbage as + * possible. + * + * @param boolean $flag use true to enable strict parsing, false to + * diable. + */ + function setStrictParsing($flag) { + self::$strict = $flag; + } + + + /** + * Get current setting for strict parsing. + * + * @return boolean true if strict parsing is in effect, false + * otherwise. + */ + function getStrictParsing() { + return self::$strict; + } + + + /** + * Enable/disable debugging output. + * + * @param boolean $flag use true to enable debug output, false to + * diable. + */ + function setDebug($flag) { + self::$debug = $flag; + } + + + /** + * Get current setting for debug output. + * + * @return boolean true if debug is enabled, false otherwise. + */ + function getDebug() { + return self::$debug; + } + + + /** + * Conditionally output debug information. + * + * This method works just like printf() except that it always + * terminates the output with a newline, and that it only outputs + * something if the {@link Pel::$debug} is true. + * + * @param string $format the format string. + * + * @param mixed $args,... any number of arguments can be given. The + * arguments will be available for the format string as usual with + * sprintf(). + */ + static function debug() { + if (self::$debug) { + $args = func_get_args(); + $str = array_shift($args); + vprintf($str . "\n", $args); + } + } + + + /** + * Conditionally output a warning. + * + * This method works just like printf() except that it prepends the + * output with the string 'Warning: ', terminates the output with a + * newline, and that it only outputs something if the PEL_DEBUG + * defined to some true value. + * + * @param string $format the format string. + * + * @param mixed $args,... any number of arguments can be given. The + * arguments will be available for the format string as usual with + * sprintf(). + */ + static function warning() { + if (self::$debug) { + $args = func_get_args(); + $str = array_shift($args); + vprintf('Warning: ' . $str . "\n", $args); + } + } + + + /** + * Translate a string. + * + * This static function will use Gettext to translate a string. By + * always using this function for static string one is assured that + * the translation will be taken from the correct text domain. + * Dynamic strings should be passed to {@link fmt} instead. + * + * @param string the string that should be translated. + * + * @return string the translated string, or the original string if + * no translation could be found. + */ + static function tra($str) { + return dgettext('pel', $str); + } + + + /** + * Translate and format a string. + * + * This static function will first use Gettext to translate a format + * string, which will then have access to any extra arguments. By + * always using this function for dynamic string one is assured that + * the translation will be taken from the correct text domain. If + * the string is static, use {@link tra} instead as it will be + * faster. + * + * @param string $format the format string. This will be translated + * before being used as a format string. + * + * @param mixed $args,... any number of arguments can be given. The + * arguments will be available for the format string as usual with + * sprintf(). + * + * @return string the translated string, or the original string if + * no translation could be found. + */ + static function fmt() { + $args = func_get_args(); + $str = array_shift($args); + return vsprintf(dgettext('pel', $str), $args); + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelConvert.php b/modules/autorotate/lib/pel/PelConvert.php new file mode 100644 index 00000000..31c4b2a6 --- /dev/null +++ b/modules/autorotate/lib/pel/PelConvert.php @@ -0,0 +1,397 @@ + + * @version $Revision: 387 $ + * @date $Date: 2005-10-05 13:02:52 +0200 (Wed, 05 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/** + * Conversion functions to and from bytes and integers. + * + * The functions found in this class are used to convert bytes into + * integers of several sizes ({@link bytesToShort}, {@link + * bytesToLong}, and {@link bytesToRational}) and convert integers of + * several sizes into bytes ({@link shortToBytes} and {@link + * longToBytes}). + * + * All the methods are static and they all rely on an argument that + * specifies the byte order to be used, this must be one of the class + * constants {@link LITTLE_ENDIAN} or {@link BIG_ENDIAN}. These + * constants will be referred to as the pseudo type PelByteOrder + * throughout the documentation. + * + * @author Martin Geisler + * @package PEL + */ +class PelConvert { + + /** + * Little-endian (Intel) byte order. + * + * Data stored in little-endian byte order store the least + * significant byte first, so the number 0x12345678 becomes 0x78 + * 0x56 0x34 0x12 when stored with little-endian byte order. + */ + const LITTLE_ENDIAN = true; + + /** + * Big-endian (Motorola) byte order. + * + * Data stored in big-endian byte order store the most significant + * byte first, so the number 0x12345678 becomes 0x12 0x34 0x56 0x78 + * when stored with big-endian byte order. + */ + const BIG_ENDIAN = false; + + + /** + * Convert an unsigned short into two bytes. + * + * @param int the unsigned short that will be converted. The lower + * two bytes will be extracted regardless of the actual size passed. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + * + * @return string the bytes representing the unsigned short. + */ + static function shortToBytes($value, $endian) { + if ($endian == self::LITTLE_ENDIAN) + return chr($value) . chr($value >> 8); + else + return chr($value >> 8) . chr($value); + } + + + /** + * Convert a signed short into two bytes. + * + * @param int the signed short that will be converted. The lower + * two bytes will be extracted regardless of the actual size passed. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + * + * @return string the bytes representing the signed short. + */ + static function sShortToBytes($value, $endian) { + /* We can just use shortToBytes, since signed shorts fits well + * within the 32 bit signed integers used in PHP. */ + return self::shortToBytes($value, $endian); + } + + + /** + * Convert an unsigned long into four bytes. + * + * Because PHP limits the size of integers to 32 bit signed, one + * cannot really have an unsigned integer in PHP. But integers + * larger than 2^31-1 will be promoted to 64 bit signed floating + * point numbers, and so such large numbers can be handled too. + * + * @param int the unsigned long that will be converted. The + * argument will be treated as an unsigned 32 bit integer and the + * lower four bytes will be extracted. Treating the argument as an + * unsigned integer means that the absolute value will be used. Use + * {@link sLongToBytes} to convert signed integers. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + * + * @return string the bytes representing the unsigned long. + */ + static function longToBytes($value, $endian) { + /* We cannot convert the number to bytes in the normal way (using + * shifts and modulo calculations) because the PHP operator >> and + * function chr() clip their arguments to 2^31-1, which is the + * largest signed integer known to PHP. But luckily base_convert + * handles such big numbers. */ + $hex = str_pad(base_convert($value, 10, 16), 8, '0', STR_PAD_LEFT); + if ($endian == self::LITTLE_ENDIAN) + return (chr(hexdec($hex{6} . $hex{7})) . + chr(hexdec($hex{4} . $hex{5})) . + chr(hexdec($hex{2} . $hex{3})) . + chr(hexdec($hex{0} . $hex{1}))); + else + return (chr(hexdec($hex{0} . $hex{1})) . + chr(hexdec($hex{2} . $hex{3})) . + chr(hexdec($hex{4} . $hex{5})) . + chr(hexdec($hex{6} . $hex{7}))); + } + + + /** + * Convert a signed long into four bytes. + * + * @param int the signed long that will be converted. The argument + * will be treated as a signed 32 bit integer, from which the lower + * four bytes will be extracted. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + * + * @return string the bytes representing the signed long. + */ + static function sLongToBytes($value, $endian) { + /* We can convert the number into bytes in the normal way using + * shifts and modulo calculations here (in contrast with + * longToBytes) because PHP automatically handles 32 bit signed + * integers for us. */ + if ($endian == self::LITTLE_ENDIAN) + return (chr($value) . + chr($value >> 8) . + chr($value >> 16) . + chr($value >> 24)); + else + return (chr($value >> 24) . + chr($value >> 16) . + chr($value >> 8) . + chr($value)); + } + + + /** + * Extract an unsigned byte from a string of bytes. + * + * @param string the bytes. + * + * @param int the offset. The byte found at the offset will be + * returned as an integer. The must be at least one byte available + * at offset. + * + * @return int the unsigned byte found at offset, e.g., an integer + * in the range 0 to 255. + */ + static function bytesToByte($bytes, $offset) { + return ord($bytes{$offset}); + } + + + /** + * Extract a signed byte from bytes. + * + * @param string the bytes. + * + * @param int the offset. The byte found at the offset will be + * returned as an integer. The must be at least one byte available + * at offset. + * + * @return int the signed byte found at offset, e.g., an integer in + * the range -128 to 127. + */ + static function bytesToSByte($bytes, $offset) { + $n = self::bytesToByte($bytes, $offset); + if ($n > 127) + return $n - 256; + else + return $n; + } + + + /** + * Extract an unsigned short from bytes. + * + * @param string the bytes. + * + * @param int the offset. The short found at the offset will be + * returned as an integer. There must be at least two bytes + * available beginning at the offset given. + * + * @return int the unsigned short found at offset, e.g., an integer + * in the range 0 to 65535. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + */ + static function bytesToShort($bytes, $offset, $endian) { + if ($endian == self::LITTLE_ENDIAN) + return (ord($bytes{$offset+1}) * 256 + + ord($bytes{$offset})); + else + return (ord($bytes{$offset}) * 256 + + ord($bytes{$offset+1})); + } + + + /** + * Extract a signed short from bytes. + * + * @param string the bytes. + * + * @param int the offset. The short found at offset will be returned + * as an integer. There must be at least two bytes available + * beginning at the offset given. + * + * @return int the signed byte found at offset, e.g., an integer in + * the range -32768 to 32767. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + */ + static function bytesToSShort($bytes, $offset, $endian) { + $n = self::bytesToShort($bytes, $offset, $endian); + if ($n > 32767) + return $n - 65536; + else + return $n; + } + + + /** + * Extract an unsigned long from bytes. + * + * @param string the bytes. + * + * @param int the offset. The long found at offset will be returned + * as an integer. There must be at least four bytes available + * beginning at the offset given. + * + * @return int the unsigned long found at offset, e.g., an integer + * in the range 0 to 4294967295. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + */ + static function bytesToLong($bytes, $offset, $endian) { + if ($endian == self::LITTLE_ENDIAN) + return (ord($bytes{$offset+3}) * 16777216 + + ord($bytes{$offset+2}) * 65536 + + ord($bytes{$offset+1}) * 256 + + ord($bytes{$offset})); + else + return (ord($bytes{$offset}) * 16777216 + + ord($bytes{$offset+1}) * 65536 + + ord($bytes{$offset+2}) * 256 + + ord($bytes{$offset+3})); + } + + + /** + * Extract a signed long from bytes. + * + * @param string the bytes. + * + * @param int the offset. The long found at offset will be returned + * as an integer. There must be at least four bytes available + * beginning at the offset given. + * + * @return int the signed long found at offset, e.g., an integer in + * the range -2147483648 to 2147483647. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + */ + static function bytesToSLong($bytes, $offset, $endian) { + $n = self::bytesToLong($bytes, $offset, $endian); + if ($n > 2147483647) + return $n - 4294967296; + else + return $n; + } + + + /** + * Extract an unsigned rational from bytes. + * + * @param string the bytes. + * + * @param int the offset. The rational found at offset will be + * returned as an array. There must be at least eight bytes + * available beginning at the offset given. + * + * @return array the unsigned rational found at offset, e.g., an + * array with two integers in the range 0 to 4294967295. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + */ + static function bytesToRational($bytes, $offset, $endian) { + return array(self::bytesToLong($bytes, $offset, $endian), + self::bytesToLong($bytes, $offset+4, $endian)); + } + + + /** + * Extract a signed rational from bytes. + * + * @param string the bytes. + * + * @param int the offset. The rational found at offset will be + * returned as an array. There must be at least eight bytes + * available beginning at the offset given. + * + * @return array the signed rational found at offset, e.g., an array + * with two integers in the range -2147483648 to 2147483647. + * + * @param PelByteOrder one of {@link LITTLE_ENDIAN} and {@link + * BIG_ENDIAN}. + */ + static function bytesToSRational($bytes, $offset, $endian) { + return array(self::bytesToSLong($bytes, $offset, $endian), + self::bytesToSLong($bytes, $offset+4, $endian)); + } + + + /** + * Format bytes for dumping. + * + * This method is for debug output, it will format a string as a + * hexadecimal dump suitable for display on a terminal. The output + * is printed directly to standard out. + * + * @param string the bytes that will be dumped. + * + * @param int the maximum number of bytes to dump. If this is left + * out (or left to the default of 0), then the entire string will be + * dumped. + */ + static function bytesToDump($bytes, $max = 0) { + $s = strlen($bytes); + + if ($max > 0) + $s = min($max, $s); + + $line = 24; + + for ($i = 0; $i < $s; $i++) { + printf('%02X ', ord($bytes{$i})); + + if (($i+1) % $line == 0) + print("\n"); + } + print("\n"); + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelDataWindow.php b/modules/autorotate/lib/pel/PelDataWindow.php new file mode 100644 index 00000000..523f8277 --- /dev/null +++ b/modules/autorotate/lib/pel/PelDataWindow.php @@ -0,0 +1,525 @@ + + * @version $Revision: 387 $ + * @date $Date: 2005-10-05 13:02:52 +0200 (Wed, 05 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelException.php'); +require_once('PelConvert.php'); +/**#@-*/ + + +/** + * An exception thrown when an invalid offset is encountered. + * + * @package PEL + * @subpackage Exception + */ +class PelDataWindowOffsetException extends PelException {} + +/** + * An exception thrown when an invalid window is encountered. + * + * @package PEL + * @subpackage Exception + */ +class PelDataWindowWindowException extends PelException {} + +/** + * The window. + * + * @package PEL + */ +class PelDataWindow { + + /** + * The data held by this window. + * + * The string can contain any kind of data, including binary data. + * + * @var string + */ + private $data = ''; + + /** + * The byte order currently in use. + * + * This will be the byte order used when data is read using the for + * example the {@link getShort} function. It must be one of {@link + * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}. + * + * @var PelByteOrder + * @see setByteOrder, getByteOrder + */ + private $order; + + /** + * The start of the current window. + * + * All offsets used for access into the data will count from this + * offset, effectively limiting access to a window starting at this + * byte. + * + * @var int + * @see setWindowStart + */ + private $start = 0; + + /** + * The size of the current window. + * + * All offsets used for access into the data will be limited by this + * variable. A valid offset must be strictly less than this + * variable. + * + * @var int + * @see setWindowSize + */ + private $size = 0; + + + /** + * Construct a new data window with the data supplied. + * + * @param string the data that this window will contain. The data + * will be copied into the new data window. + * + * @param boolean the initial byte order of the window. This must + * be either {@link PelConvert::LITTLE_ENDIAN} or {@link + * PelConvert::BIG_ENDIAN}. This will be used when integers are + * read from the data, and it can be changed later with {@link + * setByteOrder()}. + */ + function __construct($d = '', $e = PelConvert::LITTLE_ENDIAN) { + $this->data = $d; + $this->order = $e; + $this->size = strlen($d); + } + + + /** + * Get the size of the data window. + * + * @return int the number of bytes covered by the window. The + * allowed offsets go from 0 up to this number minus one. + * + * @see getBytes() + */ + function getSize() { + return $this->size; + } + + + /** + * Change the byte order of the data. + * + * @param PelByteOrder the new byte order. This must be either + * {@link PelConvert::LITTLE_ENDIAN} or {@link + * PelConvert::BIG_ENDIAN}. + */ + function setByteOrder($o) { + $this->order = $o; + } + + + /** + * Get the currently used byte order. + * + * @return PelByteOrder this will be either {@link + * PelConvert::LITTLE_ENDIAN} or {@link PelConvert::BIG_ENDIAN}. + */ + function getByteOrder() { + return $this->order; + } + + + /* Move the start of the window forward. + * + * @param int the new start of the window. All new offsets will be + * calculated from this new start offset, and the size of the window + * will shrink to keep the end of the window in place. + */ + function setWindowStart($start) { + if ($start < 0 || $start > $this->size) + throw new PelDataWindowWindowException('Window [%d, %d] does ' . + 'not fit in window [0, %d]', + $start, $this->size, $this->size); + + $this->start += $start; + $this->size -= $start; + } + + + /** + * Adjust the size of the window. + * + * The size can only be made smaller. + * + * @param int the desired size of the window. If the argument is + * negative, the window will be shrunk by the argument. + */ + function setWindowSize($size) { + if ($size < 0) + $size += $this->size; + + if ($size < 0 || $size > $this->size) + throw new PelDataWindowWindowException('Window [0, %d] ' . + 'does not fit in window [0, %d]', + $size, $this->size); + $this->size = $size; + } + + + /** + * Make a new data window with the same data as the this window. + * + * @param mixed if an integer is supplied, then it will be the start + * of the window in the clone. If left unspecified, then the clone + * will inherit the start from this object. + * + * @param mixed if an integer is supplied, then it will be the size + * of the window in the clone. If left unspecified, then the clone + * will inherit the size from this object. + * + * @return PelDataWindow a new window that operates on the same data + * as this window, but (optionally) with a smaller window size. + */ + function getClone($start = false, $size = false) { + $c = clone $this; + + if (is_int($start)) + $c->setWindowStart($start); + + if (is_int($size)) + $c->setWindowSize($size); + + return $c; + } + + + /** + * Validate an offset against the current window. + * + * @param int the offset to be validated. If the offset is negative + * or if it is greater than or equal to the current window size, + * then a {@link PelDataWindowOffsetException} is thrown. + * + * @return void if the offset is valid nothing is returned, if it is + * invalid a new {@link PelDataWindowOffsetException} is thrown. + */ + private function validateOffset($o) { + if ($o < 0 || $o >= $this->size) + throw new PelDataWindowOffsetException('Offset %d not within [%d, %d]', + $o, 0, $this->size-1); + } + + + /** + * Return some or all bytes visible in the window. + * + * This method works just like the standard {@link substr()} + * function in PHP with the exception that it works within the + * window of accessible bytes and does strict range checking. + * + * @param int the offset to the first byte returned. If a negative + * number is given, then the counting will be from the end of the + * window. Invalid offsets will result in a {@link + * PelDataWindowOffsetException} being thrown. + * + * @param int the size of the sub-window. If a negative number is + * given, then that many bytes will be omitted from the result. + * + * @return string a subset of the bytes in the window. This will + * always return no more than {@link getSize()} bytes. + */ + function getBytes($start = false, $size = false) { + if (is_int($start)) { + if ($start < 0) + $start += $this->size; + + $this->validateOffset($start); + } else { + $start = 0; + } + + if (is_int($size)) { + if ($size <= 0) + $size += $this->size - $start; + + $this->validateOffset($start+$size); + } else { + $size = $this->size - $start; + } + + return substr($this->data, $this->start + $start, $size); + } + + + /** + * Return an unsigned byte from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first byte in the current allowed window. The last + * valid offset is equal to {@link getSize()}-1. Invalid offsets + * will result in a {@link PelDataWindowOffsetException} being + * thrown. + * + * @return int the unsigned byte found at offset. + */ + function getByte($o = 0) { + /* Validate the offset --- this throws an exception if offset is + * out of range. */ + $this->validateOffset($o); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Return an unsigned byte. */ + return PelConvert::bytesToByte($this->data, $o); + } + + + /** + * Return a signed byte from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first byte in the current allowed window. The last + * valid offset is equal to {@link getSize()}-1. Invalid offsets + * will result in a {@link PelDataWindowOffsetException} being + * thrown. + * + * @return int the signed byte found at offset. + */ + function getSByte($o = 0) { + /* Validate the offset --- this throws an exception if offset is + * out of range. */ + $this->validateOffset($o); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Return a signed byte. */ + return PelConvert::bytesToSByte($this->data, $o); + } + + + /** + * Return an unsigned short read from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first short available in the current allowed window. + * The last valid offset is equal to {@link getSize()}-2. Invalid + * offsets will result in a {@link PelDataWindowOffsetException} + * being thrown. + * + * @return int the unsigned short found at offset. + */ + function getShort($o = 0) { + /* Validate the offset+1 to see if we can safely get two bytes --- + * this throws an exception if offset is out of range. */ + $this->validateOffset($o); + $this->validateOffset($o+1); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Return an unsigned short. */ + return PelConvert::bytesToShort($this->data, $o, $this->order); + } + + + /** + * Return a signed short read from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first short available in the current allowed window. + * The last valid offset is equal to {@link getSize()}-2. Invalid + * offsets will result in a {@link PelDataWindowOffsetException} + * being thrown. + * + * @return int the signed short found at offset. + */ + function getSShort($o = 0) { + /* Validate the offset+1 to see if we can safely get two bytes --- + * this throws an exception if offset is out of range. */ + $this->validateOffset($o); + $this->validateOffset($o+1); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Return a signed short. */ + return PelConvert::bytesToSShort($this->data, $o, $this->order); + } + + + /** + * Return an unsigned long read from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first long available in the current allowed window. + * The last valid offset is equal to {@link getSize()}-4. Invalid + * offsets will result in a {@link PelDataWindowOffsetException} + * being thrown. + * + * @return int the unsigned long found at offset. + */ + function getLong($o = 0) { + /* Validate the offset+3 to see if we can safely get four bytes + * --- this throws an exception if offset is out of range. */ + $this->validateOffset($o); + $this->validateOffset($o+3); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Return an unsigned long. */ + return PelConvert::bytesToLong($this->data, $o, $this->order); + } + + + /** + * Return a signed long read from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first long available in the current allowed window. + * The last valid offset is equal to {@link getSize()}-4. Invalid + * offsets will result in a {@link PelDataWindowOffsetException} + * being thrown. + * + * @return int the signed long found at offset. + */ + function getSLong($o = 0) { + /* Validate the offset+3 to see if we can safely get four bytes + * --- this throws an exception if offset is out of range. */ + $this->validateOffset($o); + $this->validateOffset($o+3); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Return a signed long. */ + return PelConvert::bytesToSLong($this->data, $o, $this->order); + } + + + /** + * Return an unsigned rational read from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first rational available in the current allowed + * window. The last valid offset is equal to {@link getSize()}-8. + * Invalid offsets will result in a {@link + * PelDataWindowOffsetException} being thrown. + * + * @return array the unsigned rational found at offset. A rational + * number is represented as an array of two numbers: the enumerator + * and denominator. Both of these numbers will be unsigned longs. + */ + function getRational($o = 0) { + return array($this->getLong($o), $this->getLong($o+4)); + } + + + /** + * Return a signed rational read from the data. + * + * @param int the offset into the data. An offset of zero will + * return the first rational available in the current allowed + * window. The last valid offset is equal to {@link getSize()}-8. + * Invalid offsets will result in a {@link + * PelDataWindowOffsetException} being thrown. + * + * @return array the signed rational found at offset. A rational + * number is represented as an array of two numbers: the enumerator + * and denominator. Both of these numbers will be signed longs. + */ + function getSRational($o = 0) { + return array($this->getSLong($o), $this->getSLong($o+4)); + } + + + /** + * String comparison on substrings. + * + * @param int the offset into the data. An offset of zero will make + * the comparison start with the very first byte available in the + * window. The last valid offset is equal to {@link getSize()} + * minus the length of the string. If the string is too long, then + * a {@link PelDataWindowOffsetException} will be thrown. + * + * @param string the string to compare with. + * + * @return boolean true if the string given matches the data in the + * window, at the specified offset, false otherwise. The comparison + * will stop as soon as a mismatch if found. + */ + function strcmp($o, $str) { + /* Validate the offset of the final character we might have to + * check. */ + $s = strlen($str); + $this->validateOffset($o); + $this->validateOffset($o + $s - 1); + + /* Translate the offset into an offset into the data. */ + $o += $this->start; + + /* Check each character, return as soon as the answer is known. */ + for ($i = 0; $i < $s; $i++) { + if ($this->data{$o + $i} != $str{$i}) + return false; + } + + /* All characters matches each other, return true. */ + return true; + } + + + /** + * Return a string representation of the data window. + * + * @return string a description of the window with information about + * the number of bytes accessible, the total number of bytes, and + * the window start and stop. + */ + function __toString() { + return Pel::fmt('DataWindow: %d bytes in [%d, %d] of %d bytes', + $this->size, + $this->start, $this->start + $this->size, + strlen($this->data)); + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntry.php b/modules/autorotate/lib/pel/PelEntry.php new file mode 100644 index 00000000..b3c1d15c --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntry.php @@ -0,0 +1,382 @@ + + * @version $Revision: 442 $ + * @date $Date: 2006-09-17 14:45:10 +0200 (Sun, 17 Sep 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelException.php'); +require_once('PelFormat.php'); +require_once('PelTag.php'); +require_once('Pel.php'); +/**#@-*/ + + +/** + * Exception indicating a problem with an entry. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelEntryException extends PelException { + + /** + * The IFD type (if known). + * + * @var int + */ + protected $type; + + /** + * The tag of the entry (if known). + * + * @var PelTag + */ + protected $tag; + + /** + * Get the IFD type associated with the exception. + * + * @return int one of {@link PelIfd::IFD0}, {@link PelIfd::IFD1}, + * {@link PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link + * PelIfd::INTEROPERABILITY}. If no type is set, null is returned. + */ + function getIfdType() { + return $this->type; + } + + + /** + * Get the tag associated with the exception. + * + * @return PelTag the tag. If no tag is set, null is returned. + */ + function getTag() { + return $this->tag; + } + +} + + +/** + * Exception indicating that an unexpected format was found. + * + * The documentation for each tag in {@link PelTag} will detail any + * constrains. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelUnexpectedFormatException extends PelEntryException { + + /** + * Construct a new exception indicating an invalid format. + * + * @param int the type of IFD. + * + * @param PelTag the tag for which the violation was found. + * + * @param PelFormat the format found. + * + * @param PelFormat the expected format. + */ + function __construct($type, $tag, $found, $expected) { + parent::__construct('Unexpected format found for %s tag: PelFormat::%s. ' . + 'Expected PelFormat::%s instead.', + PelTag::getName($type, $tag), + strtoupper(PelFormat::getName($found)), + strtoupper(PelFormat::getName($expected))); + $this->tag = $tag; + $this->type = $type; + } +} + + +/** + * Exception indicating that an unexpected number of components was + * found. + * + * Some tags have strict limits as to the allowed number of + * components, and this exception is thrown if the data violates such + * a constraint. The documentation for each tag in {@link PelTag} + * explains the expected number of components. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelWrongComponentCountException extends PelEntryException { + + /** + * Construct a new exception indicating a wrong number of + * components. + * + * @param int the type of IFD. + * + * @param PelTag the tag for which the violation was found. + * + * @param int the number of components found. + * + * @param int the expected number of components. + */ + function __construct($type, $tag, $found, $expected) { + parent::__construct('Wrong number of components found for %s tag: %d. ' . + 'Expected %d.', + PelTag::getName($type, $tag), $found, $expected); + $this->tag = $tag; + $this->type = $type; + } +} + + +/** + * Common ancestor class of all {@link PelIfd} entries. + * + * As this class is abstract you cannot instantiate objects from it. + * It only serves as a common ancestor to define the methods common to + * all entries. The most important methods are {@link getValue()} and + * {@link setValue()}, both of which is abstract in this class. The + * descendants will give concrete implementations for them. + * + * If you have some data coming from an image (some raw bytes), then + * the static method {@link newFromData()} is helpful --- it will look + * at the data and give you a proper decendent of {@link PelEntry} + * back. + * + * If you instead want to have an entry for some data which take the + * form of an integer, a string, a byte, or some other PHP type, then + * don't use this class. You should instead create an object of the + * right subclass ({@link PelEntryShort} for short integers, {@link + * PelEntryAscii} for strings, and so on) directly. + * + * @author Martin Geisler + * @package PEL + */ +abstract class PelEntry { + + /** + * Type of IFD containing this tag. + * + * This must be one of the constants defined in {@link PelIfd}: + * {@link PelIfd::IFD0} for the main image IFD, {@link PelIfd::IFD1} + * for the thumbnail image IFD, {@link PelIfd::EXIF} for the Exif + * sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or {@link + * PelIfd::INTEROPERABILITY} for the interoperability sub-IFD. + * + * @var int + */ + protected $ifd_type; + + /** + * The bytes representing this entry. + * + * Subclasses must either override {@link getBytes()} or, if + * possible, maintain this property so that it always contains a + * true representation of the entry. + * + * @var string + */ + protected $bytes = ''; + + /** + * The {@link PelTag} of this entry. + * + * @var PelTag + */ + protected $tag; + + /** + * The {@link PelFormat} of this entry. + * + * @var PelFormat + */ + protected $format; + + /** + * The number of components of this entry. + * + * @var int + */ + protected $components; + + + /** + * Return the tag of this entry. + * + * @return PelTag the tag of this entry. + */ + function getTag() { + return $this->tag; + } + + + /** + * Return the type of IFD which holds this entry. + * + * @return int one of the constants defined in {@link PelIfd}: + * {@link PelIfd::IFD0} for the main image IFD, {@link PelIfd::IFD1} + * for the thumbnail image IFD, {@link PelIfd::EXIF} for the Exif + * sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or {@link + * PelIfd::INTEROPERABILITY} for the interoperability sub-IFD. + */ + function getIfdType() { + return $this->ifd_type; + } + + + /** + * Update the IFD type. + * + * @param int must be one of the constants defined in {@link + * PelIfd}: {@link PelIfd::IFD0} for the main image IFD, {@link + * PelIfd::IFD1} for the thumbnail image IFD, {@link PelIfd::EXIF} + * for the Exif sub-IFD, {@link PelIfd::GPS} for the GPS sub-IFD, or + * {@link PelIfd::INTEROPERABILITY} for the interoperability + * sub-IFD. + */ + function setIfdType($type) { + $this->ifd_type = $type; + } + + + /** + * Return the format of this entry. + * + * @return PelFormat the format of this entry. + */ + function getFormat() { + return $this->format; + } + + + /** + * Return the number of components of this entry. + * + * @return int the number of components of this entry. + */ + function getComponents() { + return $this->components; + } + + + /** + * Turn this entry into bytes. + * + * @param PelByteOrder the desired byte order, which must be either + * {@link Convert::LITTLE_ENDIAN} or {@link Convert::BIG_ENDIAN}. + * + * @return string bytes representing this entry. + */ + function getBytes($o) { + return $this->bytes; + } + + + /** + * Get the value of this entry as text. + * + * The value will be returned in a format suitable for presentation, + * e.g., rationals will be returned as 'x/y', ASCII strings will be + * returned as themselves etc. + * + * @param boolean some values can be returned in a long or more + * brief form, and this parameter controls that. + * + * @return string the value as text. + */ + abstract function getText($brief = false); + + + /** + * Get the value of this entry. + * + * The value returned will generally be the same as the one supplied + * to the constructor or with {@link setValue()}. For a formatted + * version of the value, one should use {@link getText()} instead. + * + * @return mixed the unformatted value. + */ + abstract function getValue(); + + + /** + * Set the value of this entry. + * + * The value should be in the same format as for the constructor. + * + * @param mixed the new value. + * + * @abstract + */ + function setValue($value) { + /* This (fake) abstract method is here to make it possible for the + * documentation to refer to PelEntry::setValue(). + * + * It cannot declared abstract in the proper PHP way, for then PHP + * wont allow subclasses to define it with two arguments (which is + * what PelEntryCopyright does). + */ + throw new PelException('setValue() is abstract.'); + } + + + /** + * Turn this entry into a string. + * + * @return string a string representation of this entry. This is + * mostly for debugging. + */ + function __toString() { + $str = Pel::fmt(" Tag: 0x%04X (%s)\n", + $this->tag, PelTag::getName($this->ifd_type, $this->tag)); + $str .= Pel::fmt(" Format : %d (%s)\n", + $this->format, PelFormat::getName($this->format)); + $str .= Pel::fmt(" Components: %d\n", $this->components); + if ($this->getTag() != PelTag::MAKER_NOTE && + $this->getTag() != PelTag::PRINT_IM) + $str .= Pel::fmt(" Value : %s\n", print_r($this->getValue(), true)); + $str .= Pel::fmt(" Text : %s\n", $this->getText()); + return $str; + } +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryAscii.php b/modules/autorotate/lib/pel/PelEntryAscii.php new file mode 100644 index 00000000..bdffe4bb --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryAscii.php @@ -0,0 +1,482 @@ + + * @version $Revision: 443 $ + * @date $Date: 2006-09-17 20:32:04 +0200 (Sun, 17 Sep 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntry.php'); +/**#@-*/ + + +/** + * Class for holding a plain ASCII string. + * + * This class can hold a single ASCII string, and it will be used as in + * + * $entry = $ifd->getEntry(PelTag::IMAGE_DESCRIPTION); + * print($entry->getValue()); + * $entry->setValue('This is my image. I like it.'); + * + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryAscii extends PelEntry { + + /** + * The string hold by this entry. + * + * This is the string that was given to the {@link __construct + * constructor} or later to {@link setValue}, without any final NULL + * character. + * + * @var string + */ + private $str; + + + /** + * Make a new PelEntry that can hold an ASCII string. + * + * @param int the tag which this entry represents. This should be + * one of the constants defined in {@link PelTag}, e.g., {@link + * PelTag::IMAGE_DESCRIPTION}, {@link PelTag::MODEL}, or any other + * tag with format {@link PelFormat::ASCII}. + * + * @param string the string that this entry will represent. The + * string must obey the same rules as the string argument to {@link + * setValue}, namely that it should be given without any trailing + * NULL character and that it must be plain 7-bit ASCII. + */ + function __construct($tag, $str = '') { + $this->tag = $tag; + $this->format = PelFormat::ASCII; + $this->setValue($str); + } + + + /** + * Give the entry a new ASCII value. + * + * This will overwrite the previous value. The value can be + * retrieved later with the {@link getValue} method. + * + * @param string the new value of the entry. This should be given + * without any trailing NULL character. The string must be plain + * 7-bit ASCII, the string should contain no high bytes. + * + * @todo Implement check for high bytes? + */ + function setValue($str) { + $this->components = strlen($str)+1; + $this->str = $str; + $this->bytes = $str . chr(0x00); + } + + + /** + * Return the ASCII string of the entry. + * + * @return string the string held, without any final NULL character. + * The string will be the same as the one given to {@link setValue} + * or to the {@link __construct constructor}. + */ + function getValue() { + return $this->str; + } + + + /** + * Return the ASCII string of the entry. + * + * This methods returns the same as {@link getValue}. + * + * @param boolean not used with ASCII entries. + * + * @return string the string held, without any final NULL character. + * The string will be the same as the one given to {@link setValue} + * or to the {@link __construct constructor}. + */ + function getText($brief = false) { + return $this->str; + } + +} + + +/** + * Class for holding a date and time. + * + * This class can hold a timestamp, and it will be used as + * in this example where the time is advanced by one week: + * + * $entry = $ifd->getEntry(PelTag::DATE_TIME_ORIGINAL); + * $time = $entry->getValue(); + * print('The image was taken on the ' . date($time, 'jS')); + * $entry->setValue($time + 7 * 24 * 3600); + * + * + * The example used a standard UNIX timestamp, which is the default + * for this class. + * + * But the Exif format defines dates outside the range of a UNIX + * timestamp (about 1970 to 2038) and so you can also get access to + * the timestamp in two other formats: a simple string or a Julian Day + * Count. Please see the Calendar extension in the PHP Manual for more + * information about the Julian Day Count. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryTime extends PelEntryAscii { + + /** + * Constant denoting a UNIX timestamp. + */ + const UNIX_TIMESTAMP = 1; + /** + * Constant denoting a Exif string. + */ + const EXIF_STRING = 2; + /** + * Constant denoting a Julian Day Count. + */ + const JULIAN_DAY_COUNT = 3; + + /** + * The Julian Day Count of the timestamp held by this entry. + * + * This is an integer counting the number of whole days since + * January 1st, 4713 B.C. The fractional part of the timestamp held + * by this entry is stored in {@link $seconds}. + * + * @var int + */ + private $day_count; + + /** + * The number of seconds into the day of the timestamp held by this + * entry. + * + * The number of whole days is stored in {@link $day_count} and the + * number of seconds left-over is stored here. + * + * @var int + */ + private $seconds; + + + /** + * Make a new entry for holding a timestamp. + * + * @param int the Exif tag which this entry represents. There are + * only three standard tags which hold timestamp, so this should be + * one of the constants {@link PelTag::DATE_TIME}, {@link + * PelTag::DATE_TIME_ORIGINAL}, or {@link + * PelTag::DATE_TIME_DIGITIZED}. + * + * @param int the timestamp held by this entry in the correct form + * as indicated by the third argument. For {@link UNIX_TIMESTAMP} + * this is an integer counting the number of seconds since January + * 1st 1970, for {@link EXIF_STRING} this is a string of the form + * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a + * floating point number where the integer part denotes the day + * count and the fractional part denotes the time of day (0.25 means + * 6:00, 0.75 means 18:00). + * + * @param int the type of the timestamp. This must be one of + * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or + * {@link JULIAN_DAY_COUNT}. + */ + function __construct($tag, $timestamp, $type = self::UNIX_TIMESTAMP) { + parent::__construct($tag); + $this->setValue($timestamp, $type); + } + + + /** + * Return the timestamp of the entry. + * + * The timestamp held by this entry is returned in one of three + * formats: as a standard UNIX timestamp (default), as a fractional + * Julian Day Count, or as a string. + * + * @param int the type of the timestamp. This must be one of + * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or + * {@link JULIAN_DAY_COUNT}. + * + * @return int the timestamp held by this entry in the correct form + * as indicated by the type argument. For {@link UNIX_TIMESTAMP} + * this is an integer counting the number of seconds since January + * 1st 1970, for {@link EXIF_STRING} this is a string of the form + * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a + * floating point number where the integer part denotes the day + * count and the fractional part denotes the time of day (0.25 means + * 6:00, 0.75 means 18:00). + */ + function getValue($type = self::UNIX_TIMESTAMP) { + switch ($type) { + case self::UNIX_TIMESTAMP: + $seconds = jdtounix($this->day_count); + if ($seconds === false) + /* jdtounix() return false if the Julian Day Count is outside + * the range of a UNIX timestamp. */ + return false; + else + return $seconds + $this->seconds; + + case self::EXIF_STRING: + list($month, $day, $year) = explode('/', jdtogregorian($this->day_count)); + $hours = (int)($this->seconds / 3600); + $minutes = (int)($this->seconds % 3600 / 60); + $seconds = $this->seconds % 60; + return sprintf('%04d:%02d:%02d %02d:%02d:%02d', + $year, $month, $day, $hours, $minutes, $seconds); + case self::JULIAN_DAY_COUNT: + return $this->day_count + $this->seconds / 86400; + default: + throw new PelInvalidArgumentException('Expected UNIX_TIMESTAMP (%d), ' . + 'EXIF_STRING (%d), or ' . + 'JULIAN_DAY_COUNT (%d) for $type, '. + 'got %d.', + self::UNIX_TIMESTAMP, + self::EXIF_STRING, + self::JULIAN_DAY_COUNT, + $type); + } + } + + + /** + * Update the timestamp held by this entry. + * + * @param int the timestamp held by this entry in the correct form + * as indicated by the third argument. For {@link UNIX_TIMESTAMP} + * this is an integer counting the number of seconds since January + * 1st 1970, for {@link EXIF_STRING} this is a string of the form + * 'YYYY:MM:DD hh:mm:ss', and for {@link JULIAN_DAY_COUNT} this is a + * floating point number where the integer part denotes the day + * count and the fractional part denotes the time of day (0.25 means + * 6:00, 0.75 means 18:00). + * + * @param int the type of the timestamp. This must be one of + * {@link UNIX_TIMESTAMP}, {@link EXIF_STRING}, or + * {@link JULIAN_DAY_COUNT}. + * + * @todo How to deal with timezones? Use the TimeZoneOffset tag + * 0x882A? + */ + function setValue($timestamp, $type = self::UNIX_TIMESTAMP) { + switch ($type) { + case self::UNIX_TIMESTAMP: + $this->day_count = unixtojd($timestamp); + $this->seconds = $timestamp % 86400; + break; + + case self::EXIF_STRING: + /* Clean the timestamp: some timestamps are broken other + * separators than ':' and ' '. */ + $d = split('[^0-9]+', $timestamp); + $this->day_count = gregoriantojd($d[1], $d[2], $d[0]); + $this->seconds = $d[3]*3600 + $d[4]*60 + $d[5]; + break; + + case self::JULIAN_DAY_COUNT: + $this->day_count = (int)floor($timestamp); + $this->seconds = (int)(86400 * ($timestamp - floor($timestamp))); + break; + + default: + throw new PelInvalidArgumentException('Expected UNIX_TIMESTAMP (%d), ' . + 'EXIF_STRING (%d), or ' . + 'JULIAN_DAY_COUNT (%d) for $type, '. + 'got %d.', + self::UNIX_TIMESTAMP, + self::EXIF_STRING, + self::JULIAN_DAY_COUNT, + $type); + } + + /* Now finally update the string which will be used when this is + * turned into bytes. */ + parent::setValue($this->getValue(self::EXIF_STRING)); + } +} + + +/** + * Class for holding copyright information. + * + * The Exif standard specifies a certain format for copyright + * information where the one {@link PelTag::COPYRIGHT copyright + * tag} holds both the photographer and editor copyrights, separated + * by a NULL character. + * + * This class is used to manipulate that tag so that the format is + * kept to the standard. A common use would be to add a new copyright + * tag to an image, since most cameras do not add this tag themselves. + * This would be done like this: + * + * + * $entry = new PelEntryCopyright('Copyright, Martin Geisler, 2004'); + * $ifd0->addEntry($entry); + * + * + * Here we only set the photographer copyright, use the optional + * second argument to specify the editor copyright. If there is only + * an editor copyright, then let the first argument be the empty + * string. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryCopyright extends PelEntryAscii { + + /** + * The photographer copyright. + * + * @var string + */ + private $photographer; + + /** + * The editor copyright. + * + * @var string + */ + private $editor; + + + /** + * Make a new entry for holding copyright information. + * + * @param string the photographer copyright. Use the empty string + * if there is no photographer copyright. + * + * @param string the editor copyright. Use the empty string if + * there is no editor copyright. + */ + function __construct($photographer = '', $editor = '') { + parent::__construct(PelTag::COPYRIGHT); + $this->setValue($photographer, $editor); + } + + + /** + * Update the copyright information. + * + * @param string the photographer copyright. Use the empty string + * if there is no photographer copyright. + * + * @param string the editor copyright. Use the empty string if + * there is no editor copyright. + */ + function setValue($photographer = '', $editor = '') { + $this->photographer = $photographer; + $this->editor = $editor; + + if ($photographer == '' && $editor != '') + $photographer = ' '; + + if ($editor == '') + parent::setValue($photographer); + else + parent::setValue($photographer . chr(0x00) . $editor); + } + + + /** + * Retrive the copyright information. + * + * The strings returned will be the same as the one used previously + * with either {@link __construct the constructor} or with {@link + * setValue}. + * + * @return array an array with two strings, the photographer and + * editor copyrights. The two fields will be returned in that + * order, so that the first array index will be the photographer + * copyright, and the second will be the editor copyright. + */ + function getValue() { + return array($this->photographer, $this->editor); + } + + + /** + * Return a text string with the copyright information. + * + * The photographer and editor copyright fields will be returned + * with a '-' in between if both copyright fields are present, + * otherwise only one of them will be returned. + * + * @param boolean if false, then the strings '(Photographer)' and + * '(Editor)' will be appended to the photographer and editor + * copyright fields (if present), otherwise the fields will be + * returned as is. + * + * @return string the copyright information in a string. + */ + function getText($brief = false) { + if ($brief) { + $p = ''; + $e = ''; + } else { + $p = ' ' . Pel::tra('(Photographer)'); + $e = ' ' . Pel::tra('(Editor)'); + } + + if ($this->photographer != '' && $this->editor != '') + return $this->photographer . $p . ' - ' . $this->editor . $e; + + if ($this->photographer != '') + return $this->photographer . $p; + + if ($this->editor != '') + return $this->editor . $e; + + return ''; + } +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryByte.php b/modules/autorotate/lib/pel/PelEntryByte.php new file mode 100644 index 00000000..f6482d0c --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryByte.php @@ -0,0 +1,275 @@ + + * @version $Revision: 419 $ + * @date $Date: 2006-02-20 17:22:36 +0100 (Mon, 20 Feb 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntryNumber.php'); +/**#@-*/ + + +/** + * Class for holding unsigned bytes. + * + * This class can hold bytes, either just a single byte or an array of + * bytes. The class will be used to manipulate any of the Exif tags + * which has format {@link PelFormat::BYTE}. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryByte extends PelEntryNumber { + + /** + * Make a new entry that can hold an unsigned byte. + * + * The method accept several integer arguments. The {@link + * getValue} method will always return an array except for when a + * single integer argument is given here. + * + * @param PelTag the tag which this entry represents. This + * should be one of the constants defined in {@link PelTag} + * which has format {@link PelFormat::BYTE}. + * + * @param int $value... the byte(s) that this entry will represent. + * The argument passed must obey the same rules as the argument to + * {@link setValue}, namely that it should be within range of an + * unsigned byte, that is between 0 and 255 (inclusive). If not, + * then a {@link PelOverflowException} will be thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->min = 0; + $this->max = 255; + $this->format = PelFormat::BYTE; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Convert a number into bytes. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + function numberToBytes($number, $order) { + return chr($number); + } + +} + + +/** + * Class for holding signed bytes. + * + * This class can hold bytes, either just a single byte or an array of + * bytes. The class will be used to manipulate any of the Exif tags + * which has format {@link PelFormat::BYTE}. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntrySByte extends PelEntryNumber { + + /** + * Make a new entry that can hold a signed byte. + * + * The method accept several integer arguments. The {@link getValue} + * method will always return an array except for when a single + * integer argument is given here. + * + * @param PelTag the tag which this entry represents. This + * should be one of the constants defined in {@link PelTag} + * which has format {@link PelFormat::BYTE}. + * + * @param int $value... the byte(s) that this entry will represent. + * The argument passed must obey the same rules as the argument to + * {@link setValue}, namely that it should be within range of a + * signed byte, that is between -128 and 127 (inclusive). If not, + * then a {@link PelOverflowException} will be thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->min = -128; + $this->max = 127; + $this->format = PelFormat::SBYTE; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Convert a number into bytes. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + function numberToBytes($number, $order) { + return chr($number); + } + +} + + +/** + * Class used to manipulate strings in the format Windows XP uses. + * + * When examining the file properties of an image in Windows XP one + * can fill in title, comment, author, keyword, and subject fields. + * Filling those fields and pressing OK will result in the data being + * written into the Exif data in the image. + * + * The data is written in a non-standard format and can thus not be + * loaded directly --- this class is needed to translate it into + * normal strings. + * + * It is important that entries from this class are only created with + * the {@link PelTag::XP_TITLE}, {@link PelTag::XP_COMMENT}, {@link + * PelTag::XP_AUTHOR}, {@link PelTag::XP_KEYWORD}, and {@link + * PelTag::XP_SUBJECT} tags. If another tag is used the data will no + * longer be correctly decoded when reloaded with PEL. (The data will + * be loaded as an {@link PelEntryByte} entry, which isn't as useful.) + * + * This class is to be used as in + * + * $entry = $ifd->getEntry(PelTag::XP_TITLE); + * print($entry->getValue()); + * $entry->setValue('My favorite cat'); + * + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryWindowsString extends PelEntry { + + /** + * The string hold by this entry. + * + * This is the string that was given to the {@link __construct + * constructor} or later to {@link setValue}, without any extra NULL + * characters or any such nonsense. + * + * @var string + */ + private $str; + + + /** + * Make a new PelEntry that can hold a Windows XP specific string. + * + * @param int the tag which this entry represents. This should be + * one of {@link PelTag::XP_TITLE}, {@link PelTag::XP_COMMENT}, + * {@link PelTag::XP_AUTHOR}, {@link PelTag::XP_KEYWORD}, and {@link + * PelTag::XP_SUBJECT} tags. If another tag is used, then this + * entry will be incorrectly reloaded as a {@link PelEntryByte}. + * + * @param string the string that this entry will represent. It will + * be passed to {@link setValue} and thus has to obey its + * requirements. + */ + function __construct($tag, $str = '') { + $this->tag = $tag; + $this->format = PelFormat::BYTE; + $this->setValue($str); + } + + + /** + * Give the entry a new value. + * + * This will overwrite the previous value. The value can be + * retrieved later with the {@link getValue} method. + * + * @param string the new value of the entry. This should be use the + * Latin-1 encoding and be given without any extra NULL characters. + */ + function setValue($str) { + $l = strlen($str); + + $this->components = 2 * ($l + 1); + $this->str = $str; + $this->bytes = ''; + for ($i = 0; $i < $l; $i++) + $this->bytes .= $str{$i} . chr(0x00); + + $this->bytes .= chr(0x00) . chr(0x00); + } + + + /** + * Return the string of the entry. + * + * @return string the string held, without any extra NULL + * characters. The string will be the same as the one given to + * {@link setValue} or to the {@link __construct constructor}. + */ + function getValue() { + return $this->str; + } + + + /** + * Return the string of the entry. + * + * This methods returns the same as {@link getValue}. + * + * @param boolean not used. + * + * @return string the string held, without any extra NULL + * characters. The string will be the same as the one given to + * {@link setValue} or to the {@link __construct constructor}. + */ + function getText($brief = false) { + return $this->str; + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryLong.php b/modules/autorotate/lib/pel/PelEntryLong.php new file mode 100644 index 00000000..1ae4b30b --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryLong.php @@ -0,0 +1,182 @@ + + * @version $Revision: 419 $ + * @date $Date: 2006-02-20 17:22:36 +0100 (Mon, 20 Feb 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntryNumber.php'); +/**#@-*/ + + +/** + * Class for holding unsigned longs. + * + * This class can hold longs, either just a single long or an array of + * longs. The class will be used to manipulate any of the Exif tags + * which can have format {@link PelFormat::LONG} like in this + * example: + * + * $w = $ifd->getEntry(PelTag::EXIF_IMAGE_WIDTH); + * $w->setValue($w->getValue() / 2); + * $h = $ifd->getEntry(PelTag::EXIF_IMAGE_HEIGHT); + * $h->setValue($h->getValue() / 2); + * + * Here the width and height is updated to 50% of their original + * values. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryLong extends PelEntryNumber { + + /** + * Make a new entry that can hold an unsigned long. + * + * The method accept its arguments in two forms: several integer + * arguments or a single array argument. The {@link getValue} + * method will always return an array except for when a single + * integer argument is given here, or when an array with just a + * single integer is given. + * + * This means that one can conveniently use objects like this: + * + * $a = new PelEntryLong(PelTag::EXIF_IMAGE_WIDTH, 123456); + * $b = $a->getValue() - 654321; + * + * where the call to {@link getValue} will return an integer instead + * of an array with one integer element, which would then have to be + * extracted. + * + * @param PelTag the tag which this entry represents. This + * should be one of the constants defined in {@link PelTag}, + * e.g., {@link PelTag::IMAGE_WIDTH}, or any other tag which can + * have format {@link PelFormat::LONG}. + * + * @param int $value... the long(s) that this entry will + * represent or an array of longs. The argument passed must obey + * the same rules as the argument to {@link setValue}, namely that + * it should be within range of an unsigned long (32 bit), that is + * between 0 and 4294967295 (inclusive). If not, then a {@link + * PelExifOverflowException} will be thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->min = 0; + $this->max = 4294967295; + $this->format = PelFormat::LONG; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Convert a number into bytes. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + function numberToBytes($number, $order) { + return PelConvert::longToBytes($number, $order); + } +} + + +/** + * Class for holding signed longs. + * + * This class can hold longs, either just a single long or an array of + * longs. The class will be used to manipulate any of the Exif tags + * which can have format {@link PelFormat::SLONG}. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntrySLong extends PelEntryNumber { + + /** + * Make a new entry that can hold a signed long. + * + * The method accept its arguments in two forms: several integer + * arguments or a single array argument. The {@link getValue} + * method will always return an array except for when a single + * integer argument is given here, or when an array with just a + * single integer is given. + * + * @param PelTag the tag which this entry represents. This + * should be one of the constants defined in {@link PelTag} + * which have format {@link PelFormat::SLONG}. + * + * @param int $value... the long(s) that this entry will represent + * or an array of longs. The argument passed must obey the same + * rules as the argument to {@link setValue}, namely that it should + * be within range of a signed long (32 bit), that is between + * -2147483648 and 2147483647 (inclusive). If not, then a {@link + * PelOverflowException} will be thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->min = -2147483648; + $this->max = 2147483647; + $this->format = PelFormat::SLONG; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Convert a number into bytes. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + function numberToBytes($number, $order) { + return PelConvert::sLongToBytes($number, $order); + } +} + + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryNumber.php b/modules/autorotate/lib/pel/PelEntryNumber.php new file mode 100644 index 00000000..2301bb62 --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryNumber.php @@ -0,0 +1,309 @@ + + * @version $Revision: 419 $ + * @date $Date: 2006-02-20 17:22:36 +0100 (Mon, 20 Feb 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelException.php'); +require_once('PelEntry.php'); +/**#@-*/ + + +/** + * Exception cast when numbers overflow. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelOverflowException extends PelException { + + /** + * Construct a new overflow exception. + * + * @param int the value that is out of range. + * + * @param int the minimum allowed value. + * + * @param int the maximum allowed value. + */ + function __construct($v, $min, $max) { + parent::__construct('Value %.0f out of range [%.0f, %.0f]', + $v, $min, $max); + } +} + + +/** + * Class for holding numbers. + * + * This class can hold numbers, with range checks. + * + * @author Martin Geisler + * @package PEL + */ +abstract class PelEntryNumber extends PelEntry { + + /** + * The value held by this entry. + * + * @var array + */ + protected $value = array(); + + /** + * The minimum allowed value. + * + * Any attempt to change the value below this variable will result + * in a {@link PelOverflowException} being thrown. + * + * @var int + */ + protected $min; + + /** + * The maximum allowed value. + * + * Any attempt to change the value over this variable will result in + * a {@link PelOverflowException} being thrown. + * + * @var int + */ + protected $max; + + /** + * The dimension of the number held. + * + * Normal numbers have a dimension of one, pairs have a dimension of + * two, etc. + * + * @var int + */ + protected $dimension = 1; + + + /** + * Change the value. + * + * This method can change both the number of components and the + * value of the components. Range checks will be made on the new + * value, and a {@link PelOverflowException} will be thrown if the + * value is found to be outside the legal range. + * + * The method accept several number arguments. The {@link getValue} + * method will always return an array except for when a single + * number is given here. + * + * @param int|array $value... the new value(s). This can be zero or + * more numbers, that is, either integers or arrays. The input will + * be checked to ensure that the numbers are within the valid range. + * If not, then a {@link PelOverflowException} will be thrown. + * + * @see getValue + */ + function setValue(/* $value... */) { + $value = func_get_args(); + $this->setValueArray($value); + } + + + /** + * Change the value. + * + * This method can change both the number of components and the + * value of the components. Range checks will be made on the new + * value, and a {@link PelOverflowException} will be thrown if the + * value is found to be outside the legal range. + * + * @param array the new values. The array must contain the new + * numbers. + * + * @see getValue + */ + function setValueArray($value) { + foreach ($value as $v) + $this->validateNumber($v); + + $this->components = count($value); + $this->value = $value; + } + + + /** + * Return the numeric value held. + * + * @return int|array this will either be a single number if there is + * only one component, or an array of numbers otherwise. + */ + function getValue() { + if ($this->components == 1) + return $this->value[0]; + else + return $this->value; + } + + + /** + * Validate a number. + * + * This method will check that the number given is within the range + * given my {@link getMin()} and {@link getMax()}, inclusive. If + * not, then a {@link PelOverflowException} is thrown. + * + * @param int|array the number in question. + * + * @return void nothing, but will throw a {@link + * PelOverflowException} if the number is found to be outside the + * legal range and {@link Pel::$strict} is true. + */ + function validateNumber($n) { + if ($this->dimension == 1) { + if ($n < $this->min || $n > $this->max) + Pel::maybeThrow(new PelOverflowException($n, + $this->min, + $this->max)); + } else { + for ($i = 0; $i < $this->dimension; $i++) + if ($n[$i] < $this->min || $n[$i] > $this->max) + Pel::maybeThrow(new PelOverflowException($n[$i], + $this->min, + $this->max)); + } + } + + + /** + * Add a number. + * + * This appends a number to the numbers already held by this entry, + * thereby increasing the number of components by one. + * + * @param int|array the number to be added. + */ + function addNumber($n) { + $this->validateNumber($n); + $this->value[] = $n; + $this->components++; + } + + + /** + * Convert a number into bytes. + * + * The concrete subclasses will have to implement this method so + * that the numbers represented can be turned into bytes. + * + * The method will be called once for each number held by the entry. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + abstract function numberToBytes($number, $order); + + + /** + * Turn this entry into bytes. + * + * @param PelByteOrder the desired byte order, which must be either + * {@link PelConvert::LITTLE_ENDIAN} or {@link + * PelConvert::BIG_ENDIAN}. + * + * @return string bytes representing this entry. + */ + function getBytes($o) { + $bytes = ''; + for ($i = 0; $i < $this->components; $i++) { + if ($this->dimension == 1) { + $bytes .= $this->numberToBytes($this->value[$i], $o); + } else { + for ($j = 0; $j < $this->dimension; $j++) { + $bytes .= $this->numberToBytes($this->value[$i][$j], $o); + } + } + } + return $bytes; + } + + + /** + * Format a number. + * + * This method is called by {@link getText} to format numbers. + * Subclasses should override this method if they need more + * sophisticated behavior than the default, which is to just return + * the number as is. + * + * @param int the number which will be formatted. + * + * @param boolean it could be that there is both a verbose and a + * brief formatting available, and this argument controls that. + * + * @return string the number formatted as a string suitable for + * display. + */ + function formatNumber($number, $brief = false) { + return $number; + } + + + /** + * Get the numeric value of this entry as text. + * + * @param boolean use brief output? The numbers will be separated + * by a single space if brief output is requested, otherwise a space + * and a comma will be used. + * + * @return string the numbers(s) held by this entry. + */ + function getText($brief = false) { + if ($this->components == 0) + return ''; + + $str = $this->formatNumber($this->value[0]); + for ($i = 1; $i < $this->components; $i++) { + $str .= ($brief ? ' ' : ', '); + $str .= $this->formatNumber($this->value[$i]); + } + + return $str; + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryRational.php b/modules/autorotate/lib/pel/PelEntryRational.php new file mode 100644 index 00000000..3dd503c8 --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryRational.php @@ -0,0 +1,290 @@ + + * @version $Revision: 419 $ + * @date $Date: 2006-02-20 17:22:36 +0100 (Mon, 20 Feb 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntryLong.php'); +/**#@-*/ + + +/** + * Class for holding unsigned rational numbers. + * + * This class can hold rational numbers, consisting of a numerator and + * denominator both of which are of type unsigned long. Each rational + * is represented by an array with just two entries: the numerator and + * the denominator, in that order. + * + * The class can hold either just a single rational or an array of + * rationals. The class will be used to manipulate any of the Exif + * tags which can have format {@link PelFormat::RATIONAL} like in this + * example: + * + * + * $resolution = $ifd->getEntry(PelTag::X_RESOLUTION); + * $resolution->setValue(array(1, 300)); + * + * + * Here the x-resolution is adjusted to 1/300, which will be 300 DPI, + * unless the {@link PelTag::RESOLUTION_UNIT resolution unit} is set + * to something different than 2 which means inches. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryRational extends PelEntryLong { + + /** + * Make a new entry that can hold an unsigned rational. + * + * @param PelTag the tag which this entry represents. This should + * be one of the constants defined in {@link PelTag}, e.g., {@link + * PelTag::X_RESOLUTION}, or any other tag which can have format + * {@link PelFormat::RATIONAL}. + * + * @param array $value... the rational(s) that this entry will + * represent. The arguments passed must obey the same rules as the + * argument to {@link setValue}, namely that each argument should be + * an array with two entries, both of which must be within range of + * an unsigned long (32 bit), that is between 0 and 4294967295 + * (inclusive). If not, then a {@link PelOverflowException} will be + * thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->format = PelFormat::RATIONAL; + $this->dimension = 2; + $this->min = 0; + $this->max = 4294967295; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Format a rational number. + * + * The rational will be returned as a string with a slash '/' + * between the numerator and denominator. + * + * @param array the rational which will be formatted. + * + * @param boolean not used. + * + * @return string the rational formatted as a string suitable for + * display. + */ + function formatNumber($number, $brief = false) { + return $number[0] . '/' . $number[1]; + } + + + /** + * Get the value of an entry as text. + * + * The value will be returned in a format suitable for presentation, + * e.g., rationals will be returned as 'x/y', ASCII strings will be + * returned as themselves etc. + * + * @param boolean some values can be returned in a long or more + * brief form, and this parameter controls that. + * + * @return string the value as text. + */ + function getText($brief = false) { + if (isset($this->value[0])) + $v = $this->value[0]; + + switch ($this->tag) { + case PelTag::FNUMBER: + //CC (e->components, 1, v); + return Pel::fmt('f/%.01f', $v[0]/$v[1]); + + case PelTag::APERTURE_VALUE: + //CC (e->components, 1, v); + //if (!v_rat.denominator) return (NULL); + return Pel::fmt('f/%.01f', pow(2, $v[0]/$v[1]/2)); + + case PelTag::FOCAL_LENGTH: + //CC (e->components, 1, v); + //if (!v_rat.denominator) return (NULL); + return Pel::fmt('%.1f mm', $v[0]/$v[1]); + + case PelTag::SUBJECT_DISTANCE: + //CC (e->components, 1, v); + //if (!v_rat.denominator) return (NULL); + return Pel::fmt('%.1f m', $v[0]/$v[1]); + + case PelTag::EXPOSURE_TIME: + //CC (e->components, 1, v); + //if (!v_rat.denominator) return (NULL); + if ($v[0]/$v[1] < 1) + return Pel::fmt('1/%d sec.', $v[1]/$v[0]); + else + return Pel::fmt('%d sec.', $v[0]/$v[1]); + + case PelTag::GPS_LATITUDE: + case PelTag::GPS_LONGITUDE: + $degrees = $this->value[0][0]/$this->value[0][1]; + $minutes = $this->value[1][0]/$this->value[1][1]; + $seconds = $this->value[2][0]/$this->value[2][1]; + + return sprintf('%s° %s\' %s" (%.2f°)', + $degrees, $minutes, $seconds, + $degrees + $minutes/60 + $seconds/3600); + + default: + return parent::getText($brief); + } + } +} + + +/** + * Class for holding signed rational numbers. + * + * This class can hold rational numbers, consisting of a numerator and + * denominator both of which are of type unsigned long. Each rational + * is represented by an array with just two entries: the numerator and + * the denominator, in that order. + * + * The class can hold either just a single rational or an array of + * rationals. The class will be used to manipulate any of the Exif + * tags which can have format {@link PelFormat::SRATIONAL}. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntrySRational extends PelEntrySLong { + + /** + * Make a new entry that can hold a signed rational. + * + * @param PelTag the tag which this entry represents. This should + * be one of the constants defined in {@link PelTag}, e.g., {@link + * PelTag::SHUTTER_SPEED_VALUE}, or any other tag which can have + * format {@link PelFormat::SRATIONAL}. + * + * @param array $value... the rational(s) that this entry will + * represent. The arguments passed must obey the same rules as the + * argument to {@link setValue}, namely that each argument should be + * an array with two entries, both of which must be within range of + * a signed long (32 bit), that is between -2147483648 and + * 2147483647 (inclusive). If not, then a {@link + * PelOverflowException} will be thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->format = PelFormat::SRATIONAL; + $this->dimension = 2; + $this->min = -2147483648; + $this->max = 2147483647; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Format a rational number. + * + * The rational will be returned as a string with a slash '/' + * between the numerator and denominator. Care is taken to display + * '-1/2' instead of the ugly but mathematically equivalent '1/-2'. + * + * @param array the rational which will be formatted. + * + * @param boolean not used. + * + * @return string the rational formatted as a string suitable for + * display. + */ + function formatNumber($number, $brief = false) { + if ($number[1] < 0) + /* Turn output like 1/-2 into -1/2. */ + return (-$number[0]) . '/' . (-$number[1]); + else + return $number[0] . '/' . $number[1]; + } + + + /** + * Get the value of an entry as text. + * + * The value will be returned in a format suitable for presentation, + * e.g., rationals will be returned as 'x/y', ASCII strings will be + * returned as themselves etc. + * + * @param boolean some values can be returned in a long or more + * brief form, and this parameter controls that. + * + * @return string the value as text. + */ + function getText($brief = false) { + if (isset($this->value[0])) + $v = $this->value[0]; + + switch ($this->tag) { + case PelTag::SHUTTER_SPEED_VALUE: + //CC (e->components, 1, v); + //if (!v_srat.denominator) return (NULL); + return Pel::fmt('%.0f/%.0f sec. (APEX: %d)', + $v[0], $v[1], pow(sqrt(2), $v[0]/$v[1])); + + case PelTag::BRIGHTNESS_VALUE: + //CC (e->components, 1, v); + // + // TODO: figure out the APEX thing, or remove this so that it is + // handled by the default clause at the bottom. + return sprintf('%d/%d', $v[0], $v[1]); + //FIXME: How do I calculate the APEX value? + + case PelTag::EXPOSURE_BIAS_VALUE: + //CC (e->components, 1, v); + //if (!v_srat.denominator) return (NULL); + return sprintf('%s%.01f', $v[0]*$v[1] > 0 ? '+' : '', $v[0]/$v[1]); + + default: + return parent::getText($brief); + } + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryShort.php b/modules/autorotate/lib/pel/PelEntryShort.php new file mode 100644 index 00000000..72e499d7 --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryShort.php @@ -0,0 +1,599 @@ + + * @version $Revision: 419 $ + * @date $Date: 2006-02-20 17:22:36 +0100 (Mon, 20 Feb 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntryNumber.php'); +require_once('PelConvert.php'); +require_once('Pel.php'); +/**#@-*/ + + +/** + * Class for holding signed shorts. + * + * This class can hold shorts, either just a single short or an array + * of shorts. The class will be used to manipulate any of the Exif + * tags which has format {@link PelFormat::SHORT} like in this + * example: + * + * + * $w = $ifd->getEntry(PelTag::EXIF_IMAGE_WIDTH); + * $w->setValue($w->getValue() / 2); + * $h = $ifd->getEntry(PelTag::EXIF_IMAGE_HEIGHT); + * $h->setValue($h->getValue() / 2); + * + * + * Here the width and height is updated to 50% of their original + * values. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryShort extends PelEntryNumber { + + /** + * Make a new entry that can hold an unsigned short. + * + * The method accept several integer arguments. The {@link + * getValue} method will always return an array except for when a + * single integer argument is given here. + * + * This means that one can conveniently use objects like this: + * + * $a = new PelEntryShort(PelTag::EXIF_IMAGE_HEIGHT, 42); + * $b = $a->getValue() + 314; + * + * where the call to {@link getValue} will return an integer + * instead of an array with one integer element, which would then + * have to be extracted. + * + * @param PelTag the tag which this entry represents. This should be + * one of the constants defined in {@link PelTag}, e.g., {@link + * PelTag::IMAGE_WIDTH}, {@link PelTag::ISO_SPEED_RATINGS}, + * or any other tag with format {@link PelFormat::SHORT}. + * + * @param int $value... the short(s) that this entry will + * represent. The argument passed must obey the same rules as the + * argument to {@link setValue}, namely that it should be within + * range of an unsigned short, that is between 0 and 65535 + * (inclusive). If not, then a {@link PelOverFlowException} will be + * thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->min = 0; + $this->max = 65535; + $this->format = PelFormat::SHORT; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Convert a number into bytes. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + function numberToBytes($number, $order) { + return PelConvert::shortToBytes($number, $order); + } + + + /** + * Get the value of an entry as text. + * + * The value will be returned in a format suitable for presentation, + * e.g., instead of returning '2' for a {@link + * PelTag::METERING_MODE} tag, 'Center-Weighted Average' is + * returned. + * + * @param boolean some values can be returned in a long or more + * brief form, and this parameter controls that. + * + * @return string the value as text. + */ + function getText($brief = false) { + switch ($this->tag) { + case PelTag::METERING_MODE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Unknown'); + case 1: + return Pel::tra('Average'); + case 2: + return Pel::tra('Center-Weighted Average'); + case 3: + return Pel::tra('Spot'); + case 4: + return Pel::tra('Multi Spot'); + case 5: + return Pel::tra('Pattern'); + case 6: + return Pel::tra('Partial'); + case 255: + return Pel::tra('Other'); + default: + return $this->value[0]; + } + + case PelTag::COMPRESSION: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 1: + return Pel::tra('Uncompressed'); + case 6: + return Pel::tra('JPEG compression'); + default: + return $this->value[0]; + + } + + case PelTag::PLANAR_CONFIGURATION: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 1: + return Pel::tra('chunky format'); + case 2: + return Pel::tra('planar format'); + default: + return $this->value[0]; + } + + case PelTag::SENSING_METHOD: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 1: + return Pel::tra('Not defined'); + case 2: + return Pel::tra('One-chip color area sensor'); + case 3: + return Pel::tra('Two-chip color area sensor'); + case 4: + return Pel::tra('Three-chip color area sensor'); + case 5: + return Pel::tra('Color sequential area sensor'); + case 7: + return Pel::tra('Trilinear sensor'); + case 8: + return Pel::tra('Color sequential linear sensor'); + default: + return $this->value[0]; + } + + case PelTag::LIGHT_SOURCE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Unknown'); + case 1: + return Pel::tra('Daylight'); + case 2: + return Pel::tra('Fluorescent'); + case 3: + return Pel::tra('Tungsten (incandescent light)'); + case 4: + return Pel::tra('Flash'); + case 9: + return Pel::tra('Fine weather'); + case 10: + return Pel::tra('Cloudy weather'); + case 11: + return Pel::tra('Shade'); + case 12: + return Pel::tra('Daylight fluorescent'); + case 13: + return Pel::tra('Day white fluorescent'); + case 14: + return Pel::tra('Cool white fluorescent'); + case 15: + return Pel::tra('White fluorescent'); + case 17: + return Pel::tra('Standard light A'); + case 18: + return Pel::tra('Standard light B'); + case 19: + return Pel::tra('Standard light C'); + case 20: + return Pel::tra('D55'); + case 21: + return Pel::tra('D65'); + case 22: + return Pel::tra('D75'); + case 24: + return Pel::tra('ISO studio tungsten'); + case 255: + return Pel::tra('Other'); + default: + return $this->value[0]; + } + + case PelTag::FOCAL_PLANE_RESOLUTION_UNIT: + case PelTag::RESOLUTION_UNIT: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 2: + return Pel::tra('Inch'); + case 3: + return Pel::tra('Centimeter'); + default: + return $this->value[0]; + } + + case PelTag::EXPOSURE_PROGRAM: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Not defined'); + case 1: + return Pel::tra('Manual'); + case 2: + return Pel::tra('Normal program'); + case 3: + return Pel::tra('Aperture priority'); + case 4: + return Pel::tra('Shutter priority'); + case 5: + return Pel::tra('Creative program (biased toward depth of field)'); + case 6: + return Pel::tra('Action program (biased toward fast shutter speed)'); + case 7: + return Pel::tra('Portrait mode (for closeup photos with the background out of focus'); + case 8: + return Pel::tra('Landscape mode (for landscape photos with the background in focus'); + default: + return $this->value[0]; + } + + case PelTag::ORIENTATION: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 1: + return Pel::tra('top - left'); + case 2: + return Pel::tra('top - right'); + case 3: + return Pel::tra('bottom - right'); + case 4: + return Pel::tra('bottom - left'); + case 5: + return Pel::tra('left - top'); + case 6: + return Pel::tra('right - top'); + case 7: + return Pel::tra('right - bottom'); + case 8: + return Pel::tra('left - bottom'); + default: + return $this->value[0]; + } + + case PelTag::YCBCR_POSITIONING: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 1: + return Pel::tra('centered'); + case 2: + return Pel::tra('co-sited'); + default: + return $this->value[0]; + } + + case PelTag::YCBCR_SUB_SAMPLING: + //CC (e->components, 2, v); + if ($this->value[0] == 2 && $this->value[1] == 1) + return 'YCbCr4:2:2'; + if ($this->value[0] == 2 && $this->value[1] == 2) + return 'YCbCr4:2:0'; + + return $this->value[0] . ', ' . $this->value[1]; + + case PelTag::PHOTOMETRIC_INTERPRETATION: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 2: + return 'RGB'; + case 6: + return 'YCbCr'; + default: + return $this->value[0]; + } + + case PelTag::COLOR_SPACE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 1: + return 'sRGB'; + case 2: + return 'Adobe RGB'; + case 0xffff: + return Pel::tra('Uncalibrated'); + default: + return $this->value[0]; + } + + case PelTag::FLASH: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0x0000: + return Pel::tra('Flash did not fire.'); + case 0x0001: + return Pel::tra('Flash fired.'); + case 0x0005: + return Pel::tra('Strobe return light not detected.'); + case 0x0007: + return Pel::tra('Strobe return light detected.'); + case 0x0009: + return Pel::tra('Flash fired, compulsory flash mode.'); + case 0x000d: + return Pel::tra('Flash fired, compulsory flash mode, return light not detected.'); + case 0x000f: + return Pel::tra('Flash fired, compulsory flash mode, return light detected.'); + case 0x0010: + return Pel::tra('Flash did not fire, compulsory flash mode.'); + case 0x0018: + return Pel::tra('Flash did not fire, auto mode.'); + case 0x0019: + return Pel::tra('Flash fired, auto mode.'); + case 0x001d: + return Pel::tra('Flash fired, auto mode, return light not detected.'); + case 0x001f: + return Pel::tra('Flash fired, auto mode, return light detected.'); + case 0x0020: + return Pel::tra('No flash function.'); + case 0x0041: + return Pel::tra('Flash fired, red-eye reduction mode.'); + case 0x0045: + return Pel::tra('Flash fired, red-eye reduction mode, return light not detected.'); + case 0x0047: + return Pel::tra('Flash fired, red-eye reduction mode, return light detected.'); + case 0x0049: + return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode.'); + case 0x004d: + return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected.'); + case 0x004f: + return Pel::tra('Flash fired, compulsory flash mode, red-eye reduction mode, return light detected.'); + case 0x0058: + return Pel::tra('Flash did not fire, auto mode, red-eye reduction mode.'); + case 0x0059: + return Pel::tra('Flash fired, auto mode, red-eye reduction mode.'); + case 0x005d: + return Pel::tra('Flash fired, auto mode, return light not detected, red-eye reduction mode.'); + case 0x005f: + return Pel::tra('Flash fired, auto mode, return light detected, red-eye reduction mode.'); + default: + return $this->value[0]; + } + + case PelTag::CUSTOM_RENDERED: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Normal process'); + case 1: + return Pel::tra('Custom process'); + default: + return $this->value[0]; + } + + case PelTag::EXPOSURE_MODE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Auto exposure'); + case 1: + return Pel::tra('Manual exposure'); + case 2: + return Pel::tra('Auto bracket'); + default: + return $this->value[0]; + } + + case PelTag::WHITE_BALANCE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Auto white balance'); + case 1: + return Pel::tra('Manual white balance'); + default: + return $this->value[0]; + } + + case PelTag::SCENE_CAPTURE_TYPE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Standard'); + case 1: + return Pel::tra('Landscape'); + case 2: + return Pel::tra('Portrait'); + case 3: + return Pel::tra('Night scene'); + default: + return $this->value[0]; + } + + case PelTag::GAIN_CONTROL: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Normal'); + case 1: + return Pel::tra('Low gain up'); + case 2: + return Pel::tra('High gain up'); + case 3: + return Pel::tra('Low gain down'); + case 4: + return Pel::tra('High gain down'); + default: + return $this->value[0]; + } + + case PelTag::SATURATION: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Normal'); + case 1: + return Pel::tra('Low saturation'); + case 2: + return Pel::tra('High saturation'); + default: + return $this->value[0]; + } + + case PelTag::CONTRAST: + case PelTag::SHARPNESS: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Normal'); + case 1: + return Pel::tra('Soft'); + case 2: + return Pel::tra('Hard'); + default: + return $this->value[0]; + } + + case PelTag::SUBJECT_DISTANCE_RANGE: + //CC (e->components, 1, v); + switch ($this->value[0]) { + case 0: + return Pel::tra('Unknown'); + case 1: + return Pel::tra('Macro'); + case 2: + return Pel::tra('Close view'); + case 3: + return Pel::tra('Distant view'); + default: + return $this->value[0]; + } + + case PelTag::SUBJECT_AREA: + switch ($this->components) { + case 2: + return Pel::fmt('(x,y) = (%d,%d)', $this->value[0], $this->value[1]); + case 3: + return Pel::fmt('Within distance %d of (x,y) = (%d,%d)', + $this->value[0], $this->value[1], $this->value[2]); + case 4: + return Pel::fmt('Within rectangle (width %d, height %d) around (x,y) = (%d,%d)', + $this->value[0], $this->value[1], + $this->value[2], $this->value[3]); + + default: + return Pel::fmt('Unexpected number of components (%d, expected 2, 3, or 4).', $this->components); + } + + default: + return parent::getText($brief); + } + } +} + + +/** + * Class for holding signed shorts. + * + * This class can hold shorts, either just a single short or an array + * of shorts. The class will be used to manipulate any of the Exif + * tags which has format {@link PelFormat::SSHORT}. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntrySShort extends PelEntryNumber { + + /** + * Make a new entry that can hold a signed short. + * + * The method accept several integer arguments. The {@link + * getValue} method will always return an array except for when a + * single integer argument is given here. + * + * @param PelTag the tag which this entry represents. This + * should be one of the constants defined in {@link PelTag} + * which has format {@link PelFormat::SSHORT}. + * + * @param int $value... the signed short(s) that this entry will + * represent. The argument passed must obey the same rules as the + * argument to {@link setValue}, namely that it should be within + * range of a signed short, that is between -32768 to 32767 + * (inclusive). If not, then a {@link PelOverFlowException} will be + * thrown. + */ + function __construct($tag /* $value... */) { + $this->tag = $tag; + $this->min = -32768; + $this->max = 32767; + $this->format = PelFormat::SSHORT; + + $value = func_get_args(); + array_shift($value); + $this->setValueArray($value); + } + + + /** + * Convert a number into bytes. + * + * @param int the number that should be converted. + * + * @param PelByteOrder one of {@link PelConvert::LITTLE_ENDIAN} and + * {@link PelConvert::BIG_ENDIAN}, specifying the target byte order. + * + * @return string bytes representing the number given. + */ + function numberToBytes($number, $order) { + return PelConvert::sShortToBytes($number, $order); + } +} + + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelEntryUndefined.php b/modules/autorotate/lib/pel/PelEntryUndefined.php new file mode 100644 index 00000000..8ee8df34 --- /dev/null +++ b/modules/autorotate/lib/pel/PelEntryUndefined.php @@ -0,0 +1,416 @@ + + * @version $Revision: 380 $ + * @date $Date: 2005-10-03 14:01:28 +0200 (Mon, 03 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntry.php'); +/**#@-*/ + + +/** + * Class for holding data of any kind. + * + * This class can hold bytes of undefined format. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryUndefined extends PelEntry { + + /** + * Make a new PelEntry that can hold undefined data. + * + * @param PelTag the tag which this entry represents. This + * should be one of the constants defined in {@link PelTag}, + * e.g., {@link PelTag::SCENE_TYPE}, {@link + * PelTag::MAKER_NOTE} or any other tag with format {@link + * PelFormat::UNDEFINED}. + * + * @param string the data that this entry will be holding. Since + * the format is undefined, no checking will be done on the data. + */ + function __construct($tag, $data = '') { + $this->tag = $tag; + $this->format = PelFormat::UNDEFINED; + $this->setValue($data); + } + + + /** + * Set the data of this undefined entry. + * + * @param string the data that this entry will be holding. Since + * the format is undefined, no checking will be done on the data. + */ + function setValue($data) { + $this->components = strlen($data); + $this->bytes = $data; + } + + + /** + * Get the data of this undefined entry. + * + * @return string the data that this entry is holding. + */ + function getValue() { + return $this->bytes; + } + + + /** + * Get the value of this entry as text. + * + * The value will be returned in a format suitable for presentation. + * + * @param boolean some values can be returned in a long or more + * brief form, and this parameter controls that. + * + * @return string the value as text. + */ + function getText($brief = false) { + switch ($this->tag) { + case PelTag::FILE_SOURCE: + //CC (e->components, 1, v); + switch (ord($this->bytes{0})) { + case 0x03: + return 'DSC'; + default: + return sprintf('0x%02X', ord($this->bytes{0})); + } + + case PelTag::SCENE_TYPE: + //CC (e->components, 1, v); + switch (ord($this->bytes{0})) { + case 0x01: + return 'Directly photographed'; + default: + return sprintf('0x%02X', ord($this->bytes{0})); + } + + case PelTag::COMPONENTS_CONFIGURATION: + //CC (e->components, 4, v); + $v = ''; + for ($i = 0; $i < 4; $i++) { + switch (ord($this->bytes{$i})) { + case 0: + $v .= '-'; + break; + case 1: + $v .= 'Y'; + break; + case 2: + $v .= 'Cb'; + break; + case 3: + $v .= 'Cr'; + break; + case 4: + $v .= 'R'; + break; + case 5: + $v .= 'G'; + break; + case 6: + $v .= 'B'; + break; + default: + $v .= 'reserved'; + break; + } + if ($i < 3) $v .= ' '; + } + return $v; + + case PelTag::MAKER_NOTE: + // TODO: handle maker notes. + return $this->components . ' bytes unknown MakerNote data'; + + default: + return '(undefined)'; + } + } + +} + + +/** + * Class for a user comment. + * + * This class is used to hold user comments, which can come in several + * different character encodings. The Exif standard specifies a + * certain format of the {@link PelTag::USER_COMMENT user comment + * tag}, and this class will make sure that the format is kept. + * + * The most basic use of this class simply stores an ASCII encoded + * string for later retrieval using {@link getValue}: + * + * + * $entry = new PelEntryUserComment('An ASCII string'); + * echo $entry->getValue(); + * + * + * The string can be encoded with a different encoding, and if so, the + * encoding must be given using the second argument. The Exif + * standard specifies three known encodings: 'ASCII', 'JIS', and + * 'Unicode'. If the user comment is encoded using a character + * encoding different from the tree known encodings, then the empty + * string should be passed as encoding, thereby specifying that the + * encoding is undefined. + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryUserComment extends PelEntryUndefined { + + /** + * The user comment. + * + * @var string + */ + private $comment; + + /** + * The encoding. + * + * This should be one of 'ASCII', 'JIS', 'Unicode', or ''. + * + * @var string + */ + private $encoding; + + /** + * Make a new entry for holding a user comment. + * + * @param string the new user comment. + * + * @param string the encoding of the comment. This should be either + * 'ASCII', 'JIS', 'Unicode', or the empty string specifying an + * undefined encoding. + */ + function __construct($comment = '', $encoding = 'ASCII') { + parent::__construct(PelTag::USER_COMMENT); + $this->setValue($comment, $encoding); + } + + + /** + * Set the user comment. + * + * @param string the new user comment. + * + * @param string the encoding of the comment. This should be either + * 'ASCII', 'JIS', 'Unicode', or the empty string specifying an + * unknown encoding. + */ + function setValue($comment = '', $encoding = 'ASCII') { + $this->comment = $comment; + $this->encoding = $encoding; + parent::setValue(str_pad($encoding, 8, chr(0)) . $comment); + } + + + /** + * Returns the user comment. + * + * The comment is returned with the same character encoding as when + * it was set using {@link setValue} or {@link __construct the + * constructor}. + * + * @return string the user comment. + */ + function getValue() { + return $this->comment; + } + + + /** + * Returns the encoding. + * + * @return string the encoding of the user comment. + */ + function getEncoding() { + return $this->encoding; + } + + + /** + * Returns the user comment. + * + * @return string the user comment. + */ + function getText($brief = false) { + return $this->comment; + } + +} + + +/** + * Class to hold version information. + * + * There are three Exif entries that hold version information: the + * {@link PelTag::EXIF_VERSION}, {@link + * PelTag::FLASH_PIX_VERSION}, and {@link + * PelTag::INTEROPERABILITY_VERSION} tags. This class manages + * those tags. + * + * The class is used in a very straight-forward way: + * + * $entry = new PelEntryVersion(PelTag::EXIF_VERSION, 2.2); + * + * This creates an entry for an file complying to the Exif 2.2 + * standard. It is easy to test for standards level of an unknown + * entry: + * + * if ($entry->getTag() == PelTag::EXIF_VERSION && + * $entry->getValue() > 2.0) { + * echo 'Recent Exif version.'; + * } + * + * + * @author Martin Geisler + * @package PEL + */ +class PelEntryVersion extends PelEntryUndefined { + + /** + * The version held by this entry. + * + * @var float + */ + private $version; + + + /** + * Make a new entry for holding a version. + * + * @param PelTag the tag. This should be one of {@link + * PelTag::EXIF_VERSION}, {@link PelTag::FLASH_PIX_VERSION}, + * or {@link PelTag::INTEROPERABILITY_VERSION}. + * + * @param float the version. The size of the entries leave room for + * exactly four digits: two digits on either side of the decimal + * point. + */ + function __construct($tag, $version = 0.0) { + parent::__construct($tag); + $this->setValue($version); + } + + + /** + * Set the version held by this entry. + * + * @param float the version. The size of the entries leave room for + * exactly four digits: two digits on either side of the decimal + * point. + */ + function setValue($version = 0.0) { + $this->version = $version; + $major = floor($version); + $minor = ($version - $major)*100; + parent::setValue(sprintf('%02.0f%02.0f', $major, $minor)); + } + + + /** + * Return the version held by this entry. + * + * @return float the version. This will be the same as the value + * given to {@link setValue} or {@link __construct the + * constructor}. + */ + function getValue() { + return $this->version; + } + + + /** + * Return a text string with the version. + * + * @param boolean controls if the output should be brief. Brief + * output omits the word 'Version' so the result is just 'Exif x.y' + * instead of 'Exif Version x.y' if the entry holds information + * about the Exif version --- the output for FlashPix is similar. + * + * @return string the version number with the type of the tag, + * either 'Exif' or 'FlashPix'. + */ + function getText($brief = false) { + $v = $this->version; + + /* Versions numbers like 2.0 would be output as just 2 if we don't + * add the '.0' ourselves. */ + if (floor($this->version) == $this->version) + $v .= '.0'; + + switch ($this->tag) { + case PelTag::EXIF_VERSION: + if ($brief) + return Pel::fmt('Exif %s', $v); + else + return Pel::fmt('Exif Version %s', $v); + + case PelTag::FLASH_PIX_VERSION: + if ($brief) + return Pel::fmt('FlashPix %s', $v); + else + return Pel::fmt('FlashPix Version %s', $v); + + case PelTag::INTEROPERABILITY_VERSION: + if ($brief) + return Pel::fmt('Interoperability %s', $v); + else + return Pel::fmt('Interoperability Version %s', $v); + } + + if ($brief) + return $v; + else + return Pel::fmt('Version %s', $v); + + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelException.php b/modules/autorotate/lib/pel/PelException.php new file mode 100644 index 00000000..b090fd0e --- /dev/null +++ b/modules/autorotate/lib/pel/PelException.php @@ -0,0 +1,87 @@ + + * @version $Revision: 396 $ + * @date $Date: 2005-10-24 00:36:10 +0200 (Mon, 24 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/** + * A printf() capable exception. + * + * This class is a simple extension of the standard Exception class in + * PHP, and all the methods defined there retain their original + * meaning. + * + * @package PEL + * @subpackage Exception + */ +class PelException extends Exception { + + /** + * Construct a new PEL exception. + * + * @param string $fmt an optional format string can be given. It + * will be used as a format string for vprintf(). The remaining + * arguments will be available for the format string as usual with + * vprintf(). + * + * @param mixed $args,... any number of arguments to be used with + * the format string. + */ + function __construct(/* fmt, args... */) { + $args = func_get_args(); + $fmt = array_shift($args); + parent::__construct(vsprintf($fmt, $args)); + } +} + + +/** + * Exception throw if invalid data is found. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelInvalidDataException extends PelException {} + +/** + * Exception throw if an invalid argument is passed. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelInvalidArgumentException extends PelException {} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelExif.php b/modules/autorotate/lib/pel/PelExif.php new file mode 100644 index 00000000..6f85d695 --- /dev/null +++ b/modules/autorotate/lib/pel/PelExif.php @@ -0,0 +1,175 @@ + + * @version $Revision: 380 $ + * @date $Date: 2005-10-03 14:01:28 +0200 (Mon, 03 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelJpegContent.php'); +require_once('PelException.php'); +require_once('PelFormat.php'); +require_once('PelEntry.php'); +require_once('PelTiff.php'); +require_once('PelIfd.php'); +require_once('PelTag.php'); +require_once('Pel.php'); +/**#@-*/ + + +/** + * Class representing Exif data. + * + * Exif data resides as {@link PelJpegContent data} and consists of a + * header followed by a number of {@link PelJpegIfd IFDs}. + * + * The interesting method in this class is {@link getTiff()} which + * will return the {@link PelTiff} object which really holds the data + * which one normally think of when talking about Exif data. This is + * because Exif data is stored as an extension of the TIFF file + * format. + * + * @author Martin Geisler + * @package PEL + */ +class PelExif extends PelJpegContent { + + /** + * Exif header. + * + * The Exif data must start with these six bytes to be considered + * valid. + */ + const EXIF_HEADER = "Exif\0\0"; + + /** + * The PelTiff object contained within. + * + * @var PelTiff + */ + private $tiff = null; + + + /** + * Construct a new Exif object. + * + * The new object will be empty --- use the {@link load()} method to + * load Exif data from a {@link PelDataWindow} object, or use the + * {@link setTiff()} to change the {@link PelTiff} object, which is + * the true holder of the Exif {@link PelEntry entries}. + */ + function __construct() { + + } + + + /** + * Load and parse Exif data. + * + * This will populate the object with Exif data, contained as a + * {@link PelTiff} object. This TIFF object can be accessed with + * the {@link getTiff()} method. + */ + function load(PelDataWindow $d) { + Pel::debug('Parsing %d bytes of Exif data...', $d->getSize()); + + /* There must be at least 6 bytes for the Exif header. */ + if ($d->getSize() < 6) + throw new PelInvalidDataException('Expected at least 6 bytes of Exif ' . + 'data, found just %d bytes.', + $d->getSize()); + + /* Verify the Exif header */ + if ($d->strcmp(0, self::EXIF_HEADER)) { + $d->setWindowStart(strlen(self::EXIF_HEADER)); + } else { + throw new PelInvalidDataException('Exif header not found.'); + } + + /* The rest of the data is TIFF data. */ + $this->tiff = new PelTiff(); + $this->tiff->load($d); + } + + + /** + * Change the TIFF information. + * + * Exif data is really stored as TIFF data, and this method can be + * used to change this data from one {@link PelTiff} object to + * another. + * + * @param PelTiff the new TIFF object. + */ + function setTiff(PelTiff $tiff) { + $this->tiff = $tiff; + } + + + /** + * Get the underlying TIFF object. + * + * The actual Exif data is stored in a {@link PelTiff} object, and + * this method provides access to it. + * + * @return PelTiff the TIFF object with the Exif data. + */ + function getTiff() { + return $this->tiff; + } + + + /** + * Produce bytes for the Exif data. + * + * @return string bytes representing this object. + */ + function getBytes() { + return self::EXIF_HEADER . $this->tiff->getBytes(); + } + + + /** + * Return a string representation of this object. + * + * @return string a string describing this object. This is mostly + * useful for debugging. + */ + function __toString() { + return Pel::tra("Dumping Exif data...\n") . + $this->tiff->__toString(); + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelFormat.php b/modules/autorotate/lib/pel/PelFormat.php new file mode 100644 index 00000000..4b2badcd --- /dev/null +++ b/modules/autorotate/lib/pel/PelFormat.php @@ -0,0 +1,225 @@ + + * @version $Revision: 380 $ + * @date $Date: 2005-10-03 14:01:28 +0200 (Mon, 03 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/** + * Namespace for functions operating on Exif formats. + * + * This class defines the constants that are to be used whenever one + * has to refer to the format of an Exif tag. They will be + * collectively denoted by the pseudo-type PelFormat throughout the + * documentation. + * + * All the methods defined here are static, and they all operate on a + * single argument which should be one of the class constants. + * + * @author Martin Geisler + * @package PEL + */ +class PelFormat { + + /** + * Unsigned byte. + * + * Each component will be an unsigned 8-bit integer with a value + * between 0 and 255. + * + * Modelled with the {@link PelEntryByte} class. + */ + const BYTE = 1; + + /** + * ASCII string. + * + * Each component will be an ASCII character. + * + * Modelled with the {@link PelEntryAscii} class. + */ + const ASCII = 2; + + /** + * Unsigned short. + * + * Each component will be an unsigned 16-bit integer with a value + * between 0 and 65535. + * + * Modelled with the {@link PelEntryShort} class. + */ + const SHORT = 3; + + /** + * Unsigned long. + * + * Each component will be an unsigned 32-bit integer with a value + * between 0 and 4294967295. + * + * Modelled with the {@link PelEntryLong} class. + */ + const LONG = 4; + + /** + * Unsigned rational number. + * + * Each component will consist of two unsigned 32-bit integers + * denoting the enumerator and denominator. Each integer will have + * a value between 0 and 4294967295. + * + * Modelled with the {@link PelEntryRational} class. + */ + const RATIONAL = 5; + + /** + * Signed byte. + * + * Each component will be a signed 8-bit integer with a value + * between -128 and 127. + * + * Modelled with the {@link PelEntrySByte} class. + */ + const SBYTE = 6; + + /** + * Undefined byte. + * + * Each component will be a byte with no associated interpretation. + * + * Modelled with the {@link PelEntryUndefined} class. + */ + const UNDEFINED = 7; + + /** + * Signed short. + * + * Each component will be a signed 16-bit integer with a value + * between -32768 and 32767. + * + * Modelled with the {@link PelEntrySShort} class. + */ + const SSHORT = 8; + + /** + * Signed long. + * + * Each component will be a signed 32-bit integer with a value + * between -2147483648 and 2147483647. + * + * Modelled with the {@link PelEntrySLong} class. + */ + const SLONG = 9; + + /** + * Signed rational number. + * + * Each component will consist of two signed 32-bit integers + * denoting the enumerator and denominator. Each integer will have + * a value between -2147483648 and 2147483647. + * + * Modelled with the {@link PelEntrySRational} class. + */ + const SRATIONAL = 10; + + /** + * Floating point number. + * + * Entries with this format are not currently implemented. + */ + const FLOAT = 11; + + /** + * Double precision floating point number. + * + * Entries with this format are not currently implemented. + */ + const DOUBLE = 12; + + + /** + * Returns the name of a format. + * + * @param PelFormat the format. + * + * @return string the name of the format, e.g., 'Ascii' for the + * {@link ASCII} format etc. + */ + static function getName($type) { + switch ($type) { + case self::ASCII: return 'Ascii'; + case self::BYTE: return 'Byte'; + case self::SHORT: return 'Short'; + case self::LONG: return 'Long'; + case self::RATIONAL: return 'Rational'; + case self::SBYTE: return 'SByte'; + case self::SSHORT: return 'SShort'; + case self::SLONG: return 'SLong'; + case self::SRATIONAL: return 'SRational'; + case self::FLOAT: return 'Float'; + case self::DOUBLE: return 'Double'; + case self::UNDEFINED: return 'Undefined'; + default: + return Pel::fmt('Unknown format: 0x%X', $type); + } + } + + + /** + * Return the size of components in a given format. + * + * @param PelFormat the format. + * + * @return the size in bytes needed to store one component with the + * given format. + */ + static function getSize($type) { + switch ($type) { + case self::ASCII: return 1; + case self::BYTE: return 1; + case self::SHORT: return 2; + case self::LONG: return 4; + case self::RATIONAL: return 8; + case self::SBYTE: return 1; + case self::SSHORT: return 2; + case self::SLONG: return 4; + case self::SRATIONAL: return 8; + case self::FLOAT: return 4; + case self::DOUBLE: return 8; + case self::UNDEFINED: return 1; + default: + return Pel::fmt('Unknown format: 0x%X', $type); + } + } + +} +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelIfd.php b/modules/autorotate/lib/pel/PelIfd.php new file mode 100644 index 00000000..86c7fad1 --- /dev/null +++ b/modules/autorotate/lib/pel/PelIfd.php @@ -0,0 +1,1200 @@ + + * @version $Revision: 443 $ + * @date $Date: 2006-09-17 20:32:04 +0200 (Sun, 17 Sep 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelEntryUndefined.php'); +require_once('PelEntryRational.php'); +require_once('PelDataWindow.php'); +require_once('PelEntryAscii.php'); +require_once('PelEntryShort.php'); +require_once('PelEntryByte.php'); +require_once('PelEntryLong.php'); +require_once('PelException.php'); +require_once('PelFormat.php'); +require_once('PelEntry.php'); +require_once('PelTag.php'); +require_once('Pel.php'); +/**#@-*/ + + +/** + * Exception indicating a general problem with the IFD. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelIfdException extends PelException {} + +/** + * Class representing an Image File Directory (IFD). + * + * {@link PelTiff TIFF data} is structured as a number of Image File + * Directories, IFDs for short. Each IFD contains a number of {@link + * PelEntry entries}, some data and finally a link to the next IFD. + * + * @author Martin Geisler + * @package PEL + */ +class PelIfd implements IteratorAggregate, ArrayAccess { + + /** + * Main image IFD. + * + * Pass this to the constructor when creating an IFD which will be + * the IFD of the main image. + */ + const IFD0 = 0; + + /** + * Thumbnail image IFD. + * + * Pass this to the constructor when creating an IFD which will be + * the IFD of the thumbnail image. + */ + const IFD1 = 1; + + /** + * Exif IFD. + * + * Pass this to the constructor when creating an IFD which will be + * the Exif sub-IFD. + */ + const EXIF = 2; + + /** + * GPS IFD. + * + * Pass this to the constructor when creating an IFD which will be + * the GPS sub-IFD. + */ + const GPS = 3; + + /** + * Interoperability IFD. + * + * Pass this to the constructor when creating an IFD which will be + * the interoperability sub-IFD. + */ + const INTEROPERABILITY = 4; + + /** + * The entries held by this directory. + * + * Each tag in the directory is represented by a {@link PelEntry} + * object in this array. + * + * @var array + */ + private $entries = array(); + + /** + * The type of this directory. + * + * Initialized in the constructor. Must be one of {@link IFD0}, + * {@link IFD1}, {@link EXIF}, {@link GPS}, or {@link + * INTEROPERABILITY}. + * + * @var int + */ + private $type; + + /** + * The next directory. + * + * This will be initialized in the constructor, or be left as null + * if this is the last directory. + * + * @var PelIfd + */ + private $next = null; + + /** + * Sub-directories pointed to by this directory. + * + * This will be an array of ({@link PelTag}, {@link PelIfd}) pairs. + * + * @var array + */ + private $sub = array(); + + /** + * The thumbnail data. + * + * This will be initialized in the constructor, or be left as null + * if there are no thumbnail as part of this directory. + * + * @var PelDataWindow + */ + private $thumb_data = null; + // TODO: use this format to choose between the + // JPEG_INTERCHANGE_FORMAT and STRIP_OFFSETS tags. + // private $thumb_format; + + + /** + * Construct a new Image File Directory (IFD). + * + * The IFD will be empty, use the {@link addEntry()} method to add + * an {@link PelEntry}. Use the {@link setNext()} method to link + * this IFD to another. + * + * @param int type the type of this IFD. Must be one of {@link + * IFD0}, {@link IFD1}, {@link EXIF}, {@link GPS}, or {@link + * INTEROPERABILITY}. An {@link PelIfdException} will be thrown + * otherwise. + */ + function __construct($type) { + if ($type != PelIfd::IFD0 && $type != PelIfd::IFD1 && + $type != PelIfd::EXIF && $type != PelIfd::GPS && + $type != PelIfd::INTEROPERABILITY) + throw new PelIfdException('Unknown IFD type: %d', $type); + + $this->type = $type; + } + + + /** + * Load data into a Image File Directory (IFD). + * + * @param PelDataWindow the data window that will provide the data. + * + * @param int the offset within the window where the directory will + * be found. + */ + function load(PelDataWindow $d, $offset) { + $thumb_offset = 0; + $thumb_length = 0; + + Pel::debug('Constructing IFD at offset %d from %d bytes...', + $offset, $d->getSize()); + + /* Read the number of entries */ + $n = $d->getShort($offset); + Pel::debug('Loading %d entries...', $n); + + $offset += 2; + + /* Check if we have enough data. */ + if ($offset + 12 * $n > $d->getSize()) { + $n = floor(($offset - $d->getSize()) / 12); + Pel::maybeThrow(new PelIfdException('Adjusted to: %d.', $n)); + } + + for ($i = 0; $i < $n; $i++) { + // TODO: increment window start instead of using offsets. + $tag = $d->getShort($offset + 12 * $i); + Pel::debug('Loading entry with tag 0x%04X: %s (%d of %d)...', + $tag, PelTag::getName($this->type, $tag), $i + 1, $n); + + switch ($tag) { + case PelTag::EXIF_IFD_POINTER: + case PelTag::GPS_INFO_IFD_POINTER: + case PelTag::INTEROPERABILITY_IFD_POINTER: + $o = $d->getLong($offset + 12 * $i + 8); + Pel::debug('Found sub IFD at offset %d', $o); + + /* Map tag to IFD type. */ + if ($tag == PelTag::EXIF_IFD_POINTER) + $type = PelIfd::EXIF; + elseif ($tag == PelTag::GPS_INFO_IFD_POINTER) + $type = PelIfd::GPS; + elseif ($tag == PelTag::INTEROPERABILITY_IFD_POINTER) + $type = PelIfd::INTEROPERABILITY; + + $this->sub[$type] = new PelIfd($type); + $this->sub[$type]->load($d, $o); + break; + case PelTag::JPEG_INTERCHANGE_FORMAT: + $thumb_offset = $d->getLong($offset + 12 * $i + 8); + $this->safeSetThumbnail($d, $thumb_offset, $thumb_length); + break; + case PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH: + $thumb_length = $d->getLong($offset + 12 * $i + 8); + $this->safeSetThumbnail($d, $thumb_offset, $thumb_length); + break; + default: + $format = $d->getShort($offset + 12 * $i + 2); + $components = $d->getLong($offset + 12 * $i + 4); + + /* The data size. If bigger than 4 bytes, the actual data is + * not in the entry but somewhere else, with the offset stored + * in the entry. + */ + $s = PelFormat::getSize($format) * $components; + if ($s > 0) { + $doff = $offset + 12 * $i + 8; + if ($s > 4) + $doff = $d->getLong($doff); + + $data = $d->getClone($doff, $s); + } else { + $data = new PelDataWindow(); + } + + try { + $entry = $this->newEntryFromData($tag, $format, $components, $data); + + if ($this->isValidTag($tag)) { + $entry->setIfdType($this->type); + $this->entries[$tag] = $entry; + } else { + Pel::maybeThrow(new PelInvalidDataException("IFD %s cannot hold\n%s", + $this->getName(), + $entry->__toString())); + } + } catch (PelException $e) { + /* Throw the exception when running in strict mode, store + * otherwise. */ + Pel::maybeThrow($e); + } + + /* The format of the thumbnail is stored in this tag. */ +// TODO: handle TIFF thumbnail. +// if ($tag == PelTag::COMPRESSION) { +// $this->thumb_format = $data->getShort(); +// } + break; + } + } + + /* Offset to next IFD */ + $o = $d->getLong($offset + 12 * $n); + Pel::debug('Current offset is %d, link at %d points to %d.', + $offset, $offset + 12 * $n, $o); + + if ($o > 0) { + /* Sanity check: we need 6 bytes */ + if ($o > $d->getSize() - 6) { + Pel::maybeThrow(new PelIfdException('Bogus offset to next IFD: ' . + '%d > %d!', + $o, $d->getSize() - 6)); + } else { + if ($this->type == PelIfd::IFD1) // IFD1 shouldn't link further... + Pel::maybeThrow(new PelIfdException('IFD1 links to another IFD!')); + + $this->next = new PelIfd(PelIfd::IFD1); + $this->next->load($d, $o); + } + } else { + Pel::debug('Last IFD.'); + } + } + + + /** + * Make a new entry from a bunch of bytes. + * + * This method will create the proper subclass of {@link PelEntry} + * corresponding to the {@link PelTag} and {@link PelFormat} given. + * The entry will be initialized with the data given. + * + * Please note that the data you pass to this method should come + * from an image, that is, it should be raw bytes. If instead you + * want to create an entry for holding, say, an short integer, then + * create a {@link PelEntryShort} object directly and load the data + * into it. + * + * A {@link PelUnexpectedFormatException} is thrown if a mismatch is + * discovered between the tag and format, and likewise a {@link + * PelWrongComponentCountException} is thrown if the number of + * components does not match the requirements of the tag. The + * requirements for a given tag (if any) can be found in the + * documentation for {@link PelTag}. + * + * @param PelTag the tag of the entry. + * + * @param PelFormat the format of the entry. + * + * @param int the components in the entry. + * + * @param PelDataWindow the data which will be used to construct the + * entry. + * + * @return PelEntry a newly created entry, holding the data given. + */ + function newEntryFromData($tag, $format, $components, PelDataWindow $data) { + + /* First handle tags for which we have a specific PelEntryXXX + * class. */ + + switch ($this->type) { + + case self::IFD0: + case self::IFD1: + case self::EXIF: + case self::INTEROPERABILITY: + + switch ($tag) { + case PelTag::DATE_TIME: + case PelTag::DATE_TIME_ORIGINAL: + case PelTag::DATE_TIME_DIGITIZED: + if ($format != PelFormat::ASCII) + throw new PelUnexpectedFormatException($this->type, $tag, $format, + PelFormat::ASCII); + + if ($components != 20) + throw new PelWrongComponentCountException($this->type, $tag, $components, 20); + + // TODO: handle timezones. + return new PelEntryTime($tag, $data->getBytes(0, -1), PelEntryTime::EXIF_STRING); + + case PelTag::COPYRIGHT: + if ($format != PelFormat::ASCII) + throw new PelUnexpectedFormatException($this->type, $tag, $format, + PelFormat::ASCII); + + $v = explode("\0", trim($data->getBytes(), ' ')); + return new PelEntryCopyright($v[0], $v[1]); + + case PelTag::EXIF_VERSION: + case PelTag::FLASH_PIX_VERSION: + case PelTag::INTEROPERABILITY_VERSION: + if ($format != PelFormat::UNDEFINED) + throw new PelUnexpectedFormatException($this->type, $tag, $format, + PelFormat::UNDEFINED); + + return new PelEntryVersion($tag, $data->getBytes() / 100); + + case PelTag::USER_COMMENT: + if ($format != PelFormat::UNDEFINED) + throw new PelUnexpectedFormatException($this->type, $tag, $format, + PelFormat::UNDEFINED); + if ($data->getSize() < 8) { + return new PelEntryUserComment(); + } else { + return new PelEntryUserComment($data->getBytes(8), + rtrim($data->getBytes(0, 8))); + } + + case PelTag::XP_TITLE: + case PelTag::XP_COMMENT: + case PelTag::XP_AUTHOR: + case PelTag::XP_KEYWORDS: + case PelTag::XP_SUBJECT: + if ($format != PelFormat::BYTE) + throw new PelUnexpectedFormatException($this->type, $tag, $format, + PelFormat::BYTE); + + $v = ''; + for ($i = 0; $i < $components; $i++) { + $b = $data->getByte($i); + /* Convert the byte to a character if it is non-null --- + * information about the character encoding of these entries + * would be very nice to have! So far my tests have shown + * that characters in the Latin-1 character set are stored in + * a single byte followed by a NULL byte. */ + if ($b != 0) + $v .= chr($b); + } + + return new PelEntryWindowsString($tag, $v); + } + + case self::GPS: + + default: + /* Then handle the basic formats. */ + switch ($format) { + case PelFormat::BYTE: + $v = new PelEntryByte($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getByte($i)); + return $v; + + case PelFormat::SBYTE: + $v = new PelEntrySByte($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getSByte($i)); + return $v; + + case PelFormat::ASCII: + return new PelEntryAscii($tag, $data->getBytes(0, -1)); + + case PelFormat::SHORT: + $v = new PelEntryShort($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getShort($i*2)); + return $v; + + case PelFormat::SSHORT: + $v = new PelEntrySShort($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getSShort($i*2)); + return $v; + + case PelFormat::LONG: + $v = new PelEntryLong($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getLong($i*4)); + return $v; + + case PelFormat::SLONG: + $v = new PelEntrySLong($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getSLong($i*4)); + return $v; + + case PelFormat::RATIONAL: + $v = new PelEntryRational($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getRational($i*8)); + return $v; + + case PelFormat::SRATIONAL: + $v = new PelEntrySRational($tag); + for ($i = 0; $i < $components; $i++) + $v->addNumber($data->getSRational($i*8)); + return $v; + + case PelFormat::UNDEFINED: + return new PelEntryUndefined($tag, $data->getBytes()); + + default: + throw new PelException('Unsupported format: %s', + PelFormat::getName($format)); + } + } + } + + + + + /** + * Extract thumbnail data safely. + * + * It is safe to call this method repeatedly with either the offset + * or the length set to zero, since it requires both of these + * arguments to be positive before the thumbnail is extracted. + * + * When both parameters are set it will check the length against the + * available data and adjust as necessary. Only then is the + * thumbnail data loaded. + * + * @param PelDataWindow the data from which the thumbnail will be + * extracted. + * + * @param int the offset into the data. + * + * @param int the length of the thumbnail. + */ + private function safeSetThumbnail(PelDataWindow $d, $offset, $length) { + /* Load the thumbnail if both the offset and the length is + * available. */ + if ($offset > 0 && $length > 0) { + /* Some images have a broken length, so we try to carefully + * check the length before we store the thumbnail. */ + if ($offset + $length > $d->getSize()) { + Pel::maybeThrow(new PelIfdException('Thumbnail length %d bytes ' . + 'adjusted to %d bytes.', + $length, + $d->getSize() - $offset)); + $length = $d->getSize() - $offset; + } + + /* Now set the thumbnail normally. */ + $this->setThumbnail($d->getClone($offset, $length)); + } + } + + + /** + * Set thumbnail data. + * + * Use this to embed an arbitrary JPEG image within this IFD. The + * data will be checked to ensure that it has a proper {@link + * PelJpegMarker::EOI} at the end. If not, then the length is + * adjusted until one if found. An {@link PelIfdException} might be + * thrown (depending on {@link Pel::$strict}) this case. + * + * @param PelDataWindow the thumbnail data. + */ + function setThumbnail(PelDataWindow $d) { + $size = $d->getSize(); + /* Now move backwards until we find the EOI JPEG marker. */ + while ($d->getByte($size - 2) != 0xFF || + $d->getByte($size - 1) != PelJpegMarker::EOI) { + $size--; + } + + if ($size != $d->getSize()) + Pel::maybeThrow(new PelIfdException('Decrementing thumbnail size ' . + 'to %d bytes', $size)); + + $this->thumb_data = $d->getClone(0, $size); + } + + + /** + * Get the type of this directory. + * + * @return int of {@link PelIfd::IFD0}, {@link PelIfd::IFD1}, {@link + * PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link + * PelIfd::INTEROPERABILITY}. + */ + function getType() { + return $this->type; + } + + + /** + * Is a given tag valid for this IFD? + * + * Different types of IFDs can contain different kinds of tags --- + * the {@link IFD0} type, for example, cannot contain a {@link + * PelTag::GPS_LONGITUDE} tag. + * + * A special exception is tags with values above 0xF000. They are + * treated as private tags and will be allowed everywhere (use this + * for testing or for implementing your own types of tags). + * + * @param PelTag the tag. + * + * @return boolean true if the tag is considered valid in this IFD, + * false otherwise. + * + * @see getValidTags() + */ + function isValidTag($tag) { + return $tag > 0xF000 || in_array($tag, $this->getValidTags()); + } + + + /** + * Returns a list of valid tags for this IFD. + * + * @return array an array of {@link PelTag}s which are valid for + * this IFD. + */ + function getValidTags() { + switch ($this->type) { + case PelIfd::IFD0: + case PelIfd::IFD1: + return array(PelTag::IMAGE_WIDTH, + PelTag::IMAGE_LENGTH, + PelTag::BITS_PER_SAMPLE, + PelTag::COMPRESSION, + PelTag::PHOTOMETRIC_INTERPRETATION, + PelTag::IMAGE_DESCRIPTION, + PelTag::MAKE, + PelTag::MODEL, + PelTag::STRIP_OFFSETS, + PelTag::ORIENTATION, + PelTag::SAMPLES_PER_PIXEL, + PelTag::ROWS_PER_STRIP, + PelTag::STRIP_BYTE_COUNTS, + PelTag::X_RESOLUTION, + PelTag::Y_RESOLUTION, + PelTag::PLANAR_CONFIGURATION, + PelTag::RESOLUTION_UNIT, + PelTag::TRANSFER_FUNCTION, + PelTag::SOFTWARE, + PelTag::DATE_TIME, + PelTag::ARTIST, + PelTag::WHITE_POINT, + PelTag::PRIMARY_CHROMATICITIES, + PelTag::JPEG_INTERCHANGE_FORMAT, + PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH, + PelTag::YCBCR_COEFFICIENTS, + PelTag::YCBCR_SUB_SAMPLING, + PelTag::YCBCR_POSITIONING, + PelTag::REFERENCE_BLACK_WHITE, + PelTag::COPYRIGHT, + PelTag::EXIF_IFD_POINTER, + PelTag::GPS_INFO_IFD_POINTER, + PelTag::PRINT_IM); + + case PelIfd::EXIF: + return array(PelTag::EXPOSURE_TIME, + PelTag::FNUMBER, + PelTag::EXPOSURE_PROGRAM, + PelTag::SPECTRAL_SENSITIVITY, + PelTag::ISO_SPEED_RATINGS, + PelTag::OECF, + PelTag::EXIF_VERSION, + PelTag::DATE_TIME_ORIGINAL, + PelTag::DATE_TIME_DIGITIZED, + PelTag::COMPONENTS_CONFIGURATION, + PelTag::COMPRESSED_BITS_PER_PIXEL, + PelTag::SHUTTER_SPEED_VALUE, + PelTag::APERTURE_VALUE, + PelTag::BRIGHTNESS_VALUE, + PelTag::EXPOSURE_BIAS_VALUE, + PelTag::MAX_APERTURE_VALUE, + PelTag::SUBJECT_DISTANCE, + PelTag::METERING_MODE, + PelTag::LIGHT_SOURCE, + PelTag::FLASH, + PelTag::FOCAL_LENGTH, + PelTag::MAKER_NOTE, + PelTag::USER_COMMENT, + PelTag::SUB_SEC_TIME, + PelTag::SUB_SEC_TIME_ORIGINAL, + PelTag::SUB_SEC_TIME_DIGITIZED, + PelTag::XP_TITLE, + PelTag::XP_COMMENT, + PelTag::XP_AUTHOR, + PelTag::XP_KEYWORDS, + PelTag::XP_SUBJECT, + PelTag::FLASH_PIX_VERSION, + PelTag::COLOR_SPACE, + PelTag::PIXEL_X_DIMENSION, + PelTag::PIXEL_Y_DIMENSION, + PelTag::RELATED_SOUND_FILE, + PelTag::FLASH_ENERGY, + PelTag::SPATIAL_FREQUENCY_RESPONSE, + PelTag::FOCAL_PLANE_X_RESOLUTION, + PelTag::FOCAL_PLANE_Y_RESOLUTION, + PelTag::FOCAL_PLANE_RESOLUTION_UNIT, + PelTag::SUBJECT_LOCATION, + PelTag::EXPOSURE_INDEX, + PelTag::SENSING_METHOD, + PelTag::FILE_SOURCE, + PelTag::SCENE_TYPE, + PelTag::CFA_PATTERN, + PelTag::CUSTOM_RENDERED, + PelTag::EXPOSURE_MODE, + PelTag::WHITE_BALANCE, + PelTag::DIGITAL_ZOOM_RATIO, + PelTag::FOCAL_LENGTH_IN_35MM_FILM, + PelTag::SCENE_CAPTURE_TYPE, + PelTag::GAIN_CONTROL, + PelTag::CONTRAST, + PelTag::SATURATION, + PelTag::SHARPNESS, + PelTag::DEVICE_SETTING_DESCRIPTION, + PelTag::SUBJECT_DISTANCE_RANGE, + PelTag::IMAGE_UNIQUE_ID, + PelTag::INTEROPERABILITY_IFD_POINTER, + PelTag::GAMMA); + + case PelIfd::GPS: + return array(PelTag::GPS_VERSION_ID, + PelTag::GPS_LATITUDE_REF, + PelTag::GPS_LATITUDE, + PelTag::GPS_LONGITUDE_REF, + PelTag::GPS_LONGITUDE, + PelTag::GPS_ALTITUDE_REF, + PelTag::GPS_ALTITUDE, + PelTag::GPS_TIME_STAMP, + PelTag::GPS_SATELLITES, + PelTag::GPS_STATUS, + PelTag::GPS_MEASURE_MODE, + PelTag::GPS_DOP, + PelTag::GPS_SPEED_REF, + PelTag::GPS_SPEED, + PelTag::GPS_TRACK_REF, + PelTag::GPS_TRACK, + PelTag::GPS_IMG_DIRECTION_REF, + PelTag::GPS_IMG_DIRECTION, + PelTag::GPS_MAP_DATUM, + PelTag::GPS_DEST_LATITUDE_REF, + PelTag::GPS_DEST_LATITUDE, + PelTag::GPS_DEST_LONGITUDE_REF, + PelTag::GPS_DEST_LONGITUDE, + PelTag::GPS_DEST_BEARING_REF, + PelTag::GPS_DEST_BEARING, + PelTag::GPS_DEST_DISTANCE_REF, + PelTag::GPS_DEST_DISTANCE, + PelTag::GPS_PROCESSING_METHOD, + PelTag::GPS_AREA_INFORMATION, + PelTag::GPS_DATE_STAMP, + PelTag::GPS_DIFFERENTIAL); + + case PelIfd::INTEROPERABILITY: + return array(PelTag::INTEROPERABILITY_INDEX, + PelTag::INTEROPERABILITY_VERSION, + PelTag::RELATED_IMAGE_FILE_FORMAT, + PelTag::RELATED_IMAGE_WIDTH, + PelTag::RELATED_IMAGE_LENGTH); + + /* TODO: Where do these tags belong? +PelTag::FILL_ORDER, +PelTag::DOCUMENT_NAME, +PelTag::TRANSFER_RANGE, +PelTag::JPEG_PROC, +PelTag::BATTERY_LEVEL, +PelTag::IPTC_NAA, +PelTag::INTER_COLOR_PROFILE, +PelTag::CFA_REPEAT_PATTERN_DIM, + */ + } + } + + + /** + * Get the name of an IFD type. + * + * @param int one of {@link PelIfd::IFD0}, {@link PelIfd::IFD1}, + * {@link PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link + * PelIfd::INTEROPERABILITY}. + * + * @return string the name of type. + */ + static function getTypeName($type) { + switch ($type) { + case self::IFD0: + return '0'; + case self::IFD1: + return '1'; + case self::EXIF: + return 'Exif'; + case self::GPS: + return 'GPS'; + case self::INTEROPERABILITY: + return 'Interoperability'; + default: + throw new PelIfdException('Unknown IFD type: %d', $type); + } + } + + + /** + * Get the name of this directory. + * + * @return string the name of this directory. + */ + function getName() { + return $this->getTypeName($this->type); + } + + + /** + * Adds an entry to the directory. + * + * @param PelEntry the entry that will be added. + * + * @todo The entry will be identified with its tag, so each + * directory can only contain one entry with each tag. Is this a + * bug? + */ + function addEntry(PelEntry $e) { + $this->entries[$e->getTag()] = $e; + } + + + /** + * Does a given tag exist in this IFD? + * + * This methods is part of the ArrayAccess SPL interface for + * overriding array access of objects, it allows you to check for + * existance of an entry in the IFD: + * + * + * if (isset($ifd[PelTag::FNUMBER])) + * // ... do something with the F-number. + * + * + * @param PelTag the offset to check. + * + * @return boolean whether the tag exists. + */ + function offsetExists($tag) { + return isset($this->entries[$tag]); + } + + + /** + * Retrieve a given tag from this IFD. + * + * This methods is part of the ArrayAccess SPL interface for + * overriding array access of objects, it allows you to read entries + * from the IFD the same was as for an array: + * + * + * $entry = $ifd[PelTag::FNUMBER]; + * + * + * @param PelTag the tag to return. It is an error to ask for a tag + * which is not in the IFD, just like asking for a non-existant + * array entry. + * + * @return PelEntry the entry. + */ + function offsetGet($tag) { + return $this->entries[$tag]; + } + + + /** + * Set or update a given tag in this IFD. + * + * This methods is part of the ArrayAccess SPL interface for + * overriding array access of objects, it allows you to add new + * entries or replace esisting entries by doing: + * + * + * $ifd[PelTag::EXPOSURE_BIAS_VALUE] = $entry; + * + * + * Note that the actual array index passed is ignored! Instead the + * {@link PelTag} from the entry is used. + * + * @param PelTag the offset to update. + * + * @param PelEntry the new value. + */ + function offsetSet($tag, $e) { + if ($e instanceof PelEntry) { + $tag = $e->getTag(); + $this->entries[$tag] = $e; + } else { + throw new PelInvalidArgumentException('Argument "%s" must be a PelEntry.', $e); + } + } + + + /** + * Unset a given tag in this IFD. + * + * This methods is part of the ArrayAccess SPL interface for + * overriding array access of objects, it allows you to delete + * entries in the IFD by doing: + * + * + * unset($ifd[PelTag::EXPOSURE_BIAS_VALUE]) + * + * + * @param PelTag the offset to delete. + */ + function offsetUnset($tag) { + unset($this->entries[$tag]); + } + + + /** + * Retrieve an entry. + * + * @param PelTag the tag identifying the entry. + * + * @return PelEntry the entry associated with the tag, or null if no + * such entry exists. + */ + function getEntry($tag) { + if (isset($this->entries[$tag])) + return $this->entries[$tag]; + else + return null; + } + + + /** + * Returns all entries contained in this IFD. + * + * @return array an array of {@link PelEntry} objects, or rather + * descendant classes. The array has {@link PelTag}s as keys + * and the entries as values. + * + * @see getEntry + * @see getIterator + */ + function getEntries() { + return $this->entries; + } + + + /** + * Return an iterator for all entries contained in this IFD. + * + * Used with foreach as in + * + * + * foreach ($ifd as $tag => $entry) { + * // $tag is now a PelTag and $entry is a PelEntry object. + * } + * + * + * @return Iterator an iterator using the {@link PelTag tags} as + * keys and the entries as values. + */ + function getIterator() { + return new ArrayIterator($this->entries); + } + + + /** + * Returns available thumbnail data. + * + * @return string the bytes in the thumbnail, if any. If the IFD + * does not contain any thumbnail data, the empty string is + * returned. + * + * @todo Throw an exception instead when no data is available? + * + * @todo Return the $this->thumb_data object instead of the bytes? + */ + function getThumbnailData() { + if ($this->thumb_data != null) + return $this->thumb_data->getBytes(); + else + return ''; + } + + + /** + * Make this directory point to a new directory. + * + * @param PelIfd the IFD that this directory will point to. + */ + function setNextIfd(PelIfd $i) { + $this->next = $i; + } + + + /** + * Return the IFD pointed to by this directory. + * + * @return PelIfd the next IFD, following this IFD. If this is the + * last IFD, null is returned. + */ + function getNextIfd() { + return $this->next; + } + + + /** + * Check if this is the last IFD. + * + * @return boolean true if there are no following IFD, false + * otherwise. + */ + function isLastIfd() { + return $this->next == null; + } + + + /** + * Add a sub-IFD. + * + * Any previous sub-IFD of the same type will be overwritten. + * + * @param PelIfd the sub IFD. The type of must be one of {@link + * PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link + * PelIfd::INTEROPERABILITY}. + */ + function addSubIfd(PelIfd $sub) { + $this->sub[$sub->type] = $sub; + } + + + /** + * Return a sub IFD. + * + * @param int the type of the sub IFD. This must be one of {@link + * PelIfd::EXIF}, {@link PelIfd::GPS}, or {@link + * PelIfd::INTEROPERABILITY}. + * + * @return PelIfd the IFD associated with the type, or null if that + * sub IFD does not exist. + */ + function getSubIfd($type) { + if (isset($this->sub[$type])) + return $this->sub[$type]; + else + return null; + } + + + /** + * Get all sub IFDs. + * + * @return array an associative array with (IFD-type, {@link + * PelIfd}) pairs. + */ + function getSubIfds() { + return $this->sub; + } + + + /** + * Turn this directory into bytes. + * + * This directory will be turned into a byte string, with the + * specified byte order. The offsets will be calculated from the + * offset given. + * + * @param int the offset of the first byte of this directory. + * + * @param PelByteOrder the byte order that should be used when + * turning integers into bytes. This should be one of {@link + * PelConvert::LITTLE_ENDIAN} and {@link PelConvert::BIG_ENDIAN}. + */ + function getBytes($offset, $order) { + $bytes = ''; + $extra_bytes = ''; + + Pel::debug('Bytes from IDF will start at offset %d within Exif data', + $offset); + + $n = count($this->entries) + count($this->sub); + if ($this->thumb_data != null) { + /* We need two extra entries for the thumbnail offset and + * length. */ + $n += 2; + } + + $bytes .= PelConvert::shortToBytes($n, $order); + + /* Initialize offset of extra data. This included the bytes + * preceding this IFD, the bytes needed for the count of entries, + * the entries themselves (and sub entries), the extra data in the + * entries, and the IFD link. + */ + $end = $offset + 2 + 12 * $n + 4; + + foreach ($this->entries as $tag => $entry) { + /* Each entry is 12 bytes long. */ + $bytes .= PelConvert::shortToBytes($entry->getTag(), $order); + $bytes .= PelConvert::shortToBytes($entry->getFormat(), $order); + $bytes .= PelConvert::longToBytes($entry->getComponents(), $order); + + /* + * Size? If bigger than 4 bytes, the actual data is not in + * the entry but somewhere else. + */ + $data = $entry->getBytes($order); + $s = strlen($data); + if ($s > 4) { + Pel::debug('Data size %d too big, storing at offset %d instead.', + $s, $end); + $bytes .= PelConvert::longToBytes($end, $order); + $extra_bytes .= $data; + $end += $s; + } else { + Pel::debug('Data size %d fits.', $s); + /* Copy data directly, pad with NULL bytes as necessary to + * fill out the four bytes available.*/ + $bytes .= $data . str_repeat(chr(0), 4 - $s); + } + } + + if ($this->thumb_data != null) { + Pel::debug('Appending %d bytes of thumbnail data at %d', + $this->thumb_data->getSize(), $end); + // TODO: make PelEntry a class that can be constructed with + // arguments corresponding to the newt four lines. + $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT_LENGTH, + $order); + $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); + $bytes .= PelConvert::longToBytes(1, $order); + $bytes .= PelConvert::longToBytes($this->thumb_data->getSize(), + $order); + + $bytes .= PelConvert::shortToBytes(PelTag::JPEG_INTERCHANGE_FORMAT, + $order); + $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); + $bytes .= PelConvert::longToBytes(1, $order); + $bytes .= PelConvert::longToBytes($end, $order); + + $extra_bytes .= $this->thumb_data->getBytes(); + $end += $this->thumb_data->getSize(); + } + + + /* Find bytes from sub IFDs. */ + $sub_bytes = ''; + foreach ($this->sub as $type => $sub) { + if ($type == PelIfd::EXIF) + $tag = PelTag::EXIF_IFD_POINTER; + elseif ($type == PelIfd::GPS) + $tag = PelTag::GPS_INFO_IFD_POINTER; + elseif ($type == PelIfd::INTEROPERABILITY) + $tag = PelTag::INTEROPERABILITY_IFD_POINTER; + + /* Make an aditional entry with the pointer. */ + $bytes .= PelConvert::shortToBytes($tag, $order); + /* Next the format, which is always unsigned long. */ + $bytes .= PelConvert::shortToBytes(PelFormat::LONG, $order); + /* There is only one component. */ + $bytes .= PelConvert::longToBytes(1, $order); + + $data = $sub->getBytes($end, $order); + $s = strlen($data); + $sub_bytes .= $data; + + $bytes .= PelConvert::longToBytes($end, $order); + $end += $s; + } + + /* Make link to next IFD, if any*/ + if ($this->isLastIFD()) { + $link = 0; + } else { + $link = $end; + } + + Pel::debug('Link to next IFD: %d', $link); + + $bytes .= PelConvert::longtoBytes($link, $order); + + $bytes .= $extra_bytes . $sub_bytes; + + if (!$this->isLastIfd()) + $bytes .= $this->next->getBytes($end, $order); + + return $bytes; + } + + + /** + * Turn this directory into text. + * + * @return string information about the directory, mainly for + * debugging. + */ + function __toString() { + $str = Pel::fmt("Dumping IFD %s with %d entries...\n", + $this->getName(), count($this->entries)); + + foreach ($this->entries as $entry) + $str .= $entry->__toString(); + + $str .= Pel::fmt("Dumping %d sub IFDs...\n", count($this->sub)); + + foreach ($this->sub as $type => $ifd) + $str .= $ifd->__toString(); + + if ($this->next != null) + $str .= $this->next->__toString(); + + return $str; + } + + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelJpeg.php b/modules/autorotate/lib/pel/PelJpeg.php new file mode 100644 index 00000000..f1fb0a7c --- /dev/null +++ b/modules/autorotate/lib/pel/PelJpeg.php @@ -0,0 +1,599 @@ + + * @version $Revision: 473 $ + * @date $Date: 2006-11-24 00:12:21 +0100 (Fri, 24 Nov 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelJpegComment.php'); +require_once('PelJpegContent.php'); +require_once('PelDataWindow.php'); +require_once('PelJpegMarker.php'); +require_once('PelException.php'); +require_once('PelExif.php'); +require_once('Pel.php'); +/**#@-*/ + + +/** + * Exception thrown when an invalid marker is found. + * + * This exception is thrown when PEL expects to find a {@link + * PelJpegMarker} and instead finds a byte that isn't a known marker. + * + * @author Martin Geisler + * @package PEL + * @subpackage Exception + */ +class PelJpegInvalidMarkerException extends PelException { + + /** + * Construct a new invalid marker exception. + * + * The exception will contain a message describing the error, + * including the byte found and the offset of the offending byte. + * + * @param int the byte found. + * + * @param int the offset in the data. + */ + function __construct($marker, $offset) { + parent::__construct('Invalid marker found at offset %d: 0x%2X', + $offset, $marker); + } +} + +/** + * Class for handling JPEG data. + * + * The {@link PelJpeg} class defined here provides an abstraction for + * dealing with a JPEG file. The file will be contain a number of + * sections containing some {@link PelJpegContent content} identified + * by a {@link PelJpegMarker marker}. + * + * The {@link getExif()} method is used get hold of the {@link + * PelJpegMarker::APP1 APP1} section which stores Exif data. So if + * the name of the JPEG file is stored in $filename, then one would + * get hold of the Exif data by saying: + * + * + * $jpeg = new PelJpeg($filename); + * $exif = $jpeg->getExif(); + * $tiff = $exif->getTiff(); + * $ifd0 = $tiff->getIfd(); + * $exif = $ifd0->getSubIfd(PelIfd::EXIF); + * $ifd1 = $ifd0->getNextIfd(); + * + * + * The $idf0 and $ifd1 variables will then be two {@link PelTiff TIFF} + * {@link PelIfd Image File Directories}, in which the data is stored + * under the keys found in {@link PelTag}. + * + * Should one have some image data (in the form of a {@link + * PelDataWindow}) of an unknown type, then the {@link + * PelJpeg::isValid()} function is handy: it will quickly test if the + * data could be valid JPEG data. The {@link PelTiff::isValid()} + * function does the same for TIFF images. + * + * @author Martin Geisler + * @package PEL + */ +class PelJpeg { + + /** + * The sections in the JPEG data. + * + * A JPEG file is built up as a sequence of sections, each section + * is identified with a {@link PelJpegMarker}. Some sections can + * occur more than once in the JPEG stream (the {@link + * PelJpegMarker::DQT DQT} and {@link PelJpegMarker::DHT DTH} + * markers for example) and so this is an array of ({@link + * PelJpegMarker}, {@link PelJpegContent}) pairs. + * + * The content can be either generic {@link PelJpegContent JPEG + * content} or {@link PelExif Exif data}. + * + * @var array + */ + private $sections = array(); + + /** + * The JPEG image data. + * + * @var PelDataWindow + */ + private $jpeg_data = null; + + /** + * Construct a new JPEG object. + * + * The new object will be empty unless an argument is given from + * which it can initialize itself. This can either be the filename + * of a JPEG image, a {@link PelDataWindow} object or a PHP image + * resource handle. + * + * New Exif data (in the form of a {@link PelExif} object) can be + * inserted with the {@link setExif()} method: + * + * + * $jpeg = new PelJpeg($data); + * // Create container for the Exif information: + * $exif = new PelExif(); + * // Now Add a PelTiff object with a PelIfd object with one or more + * // PelEntry objects to $exif... Finally add $exif to $jpeg: + * $jpeg->setExif($exif); + * + */ + function __construct($data = false) { + if ($data === false) + return; + + if (is_string($data)) { + Pel::debug('Initializing PelJpeg object from %s', $data); + $this->loadFile($data); + } elseif ($data instanceof PelDataWindow) { + Pel::debug('Initializing PelJpeg object from PelDataWindow.'); + $this->load($data); + } elseif (is_resource($data) && get_resource_type($data) == 'gd') { + Pel::debug('Initializing PelJpeg object from image resource.'); + /* The ImageJpeg() function insists on printing the bytes + * instead of returning them in a more civil way as a string, so + * we have to buffer the output... */ + ob_start(); + ImageJpeg($data); + $bytes = ob_get_clean(); + $this->load(new PelDataWindow($bytes)); + } else { + throw new PelInvalidArgumentException('Bad type for $data: %s', + gettype($data)); + } + } + + /** + * Load data into a JPEG object. + * + * The data supplied will be parsed and turned into an object + * structure representing the image. This structure can then be + * manipulated and later turned back into an string of bytes. + * + * This methods can be called at any time after a JPEG object has + * been constructed, also after the {@link appendSection()} has been + * called to append custom sections. Loading several JPEG images + * into one object will accumulate the sections, but there will only + * be one {@link PelJpegMarker::SOS} section at any given time. + * + * @param PelDataWindow the data that will be turned into JPEG + * sections. + */ + function load(PelDataWindow $d) { + + Pel::debug('Parsing %d bytes...', $d->getSize()); + + /* JPEG data is stored in big-endian format. */ + $d->setByteOrder(PelConvert::BIG_ENDIAN); + + /* Run through the data to read the sections in the image. After + * each section is read, the start of the data window will be + * moved forward, and after the last section we'll terminate with + * no data left in the window. */ + while ($d->getSize() > 0) { + /* JPEG sections start with 0xFF. The first byte that is not + * 0xFF is a marker (hopefully). + */ + for ($i = 0; $i < 7; $i++) + if ($d->getByte($i) != 0xFF) + break; + + $marker = $d->getByte($i); + + if (!PelJpegMarker::isValid($marker)) + throw new PelJpegInvalidMarkerException($marker, $i); + + /* Move window so first byte becomes first byte in this + * section. */ + $d->setWindowStart($i+1); + + if ($marker == PelJpegMarker::SOI || $marker == PelJpegMarker::EOI) { + $content = new PelJpegContent(new PelDataWindow()); + $this->appendSection($marker, $content); + } else { + /* Read the length of the section. The length includes the + * two bytes used to store the length. */ + $len = $d->getShort(0) - 2; + + Pel::debug('Found %s section of length %d', + PelJpegMarker::getName($marker), $len); + + /* Skip past the length. */ + $d->setWindowStart(2); + + if ($marker == PelJpegMarker::APP1) { + + try { + $content = new PelExif(); + $content->load($d->getClone(0, $len)); + } catch (PelInvalidDataException $e) { + /* We store the data as normal JPEG content if it could + * not be parsed as Exif data. */ + $content = new PelJpegContent($d->getClone(0, $len)); + } + + $this->appendSection($marker, $content); + /* Skip past the data. */ + $d->setWindowStart($len); + + } elseif ($marker == PelJpegMarker::COM) { + + $content = new PelJpegComment(); + $content->load($d->getClone(0, $len)); + $this->appendSection($marker, $content); + $d->setWindowStart($len); + + } else { + + $content = new PelJpegContent($d->getClone(0, $len)); + $this->appendSection($marker, $content); + /* Skip past the data. */ + $d->setWindowStart($len); + + /* In case of SOS, image data will follow. */ + if ($marker == PelJpegMarker::SOS) { + /* Some images have some trailing (garbage?) following the + * EOI marker. To handle this we seek backwards until we + * find the EOI marker. Any trailing content is stored as + * a PelJpegContent object. */ + + $length = $d->getSize(); + while ($d->getByte($length-2) != 0xFF || + $d->getByte($length-1) != PelJpegMarker::EOI) { + $length--; + } + + $this->jpeg_data = $d->getClone(0, $length-2); + Pel::debug('JPEG data: ' . $this->jpeg_data->__toString()); + + /* Append the EOI. */ + $this->appendSection(PelJpegMarker::EOI, + new PelJpegContent(new PelDataWindow())); + + /* Now check to see if there are any trailing data. */ + if ($length != $d->getSize()) { + Pel::maybeThrow(new PelException('Found trailing content ' . + 'after EOI: %d bytes', + $d->getSize() - $length)); + $content = new PelJpegContent($d->getClone($length)); + /* We don't have a proper JPEG marker for trailing + * garbage, so we just use 0x00... */ + $this->appendSection(0x00, $content); + } + + /* Done with the loop. */ + break; + } + } + } + } /* while ($d->getSize() > 0) */ + } + + + /** + * Load data from a file into a JPEG object. + * + * @param string the filename. This must be a readable file. + */ + function loadFile($filename) { + $this->load(new PelDataWindow(file_get_contents($filename))); + } + + + /** + * Set Exif data. + * + * Use this to set the Exif data in the image. This will overwrite + * any old Exif information in the image. + * + * @param PelExif the Exif data. + */ + function setExif(PelExif $exif) { + $app0_offset = 1; + $app1_offset = -1; + + /* Search through all sections looking for APP0 or APP1. */ + for ($i = 0; $i < count($this->sections); $i++) { + if ($this->sections[$i][0] == PelJpegMarker::APP0) { + $app0_offset = $i; + } elseif ($this->sections[$i][0] == PelJpegMarker::APP1) { + $app1_offset = $i; + break; + } + } + + /* Store the Exif data at the appropriate place, either where the + * old Exif data was stored ($app1_offset) or right after APP0 + * ($app0_offset+1). */ + if ($app1_offset > 0) + $this->sections[$app1_offset][1] = $exif; + else + $this->insertSection(PelJpegMarker::APP1, $exif, $app0_offset+1); + } + + + /** + * Get Exif data. + * + * Use this to get the @{link PelExif Exif data} stored. + * + * @return PelExif the Exif data found or null if the image has no + * Exif data. + */ + function getExif() { + $exif = $this->getSection(PelJpegMarker::APP1); + if ($exif instanceof PelExif) + return $exif; + else + return null; + } + + + /** + * Clear any Exif data. + * + * This method will only clear the first @{link PelJpegMarker::APP1} + * section found (there should normally be just one). + */ + function clearExif() { + for ($i = 0; $i < count($this->sections); $i++) { + if ($this->sections[$i][0] == PelJpegMarker::APP1) { + unset($this->sections[$i]); + return; + } + } + } + + + /** + * Append a new section. + * + * Used only when loading an image. If it used again later, then the + * section will end up after the @{link PelJpegMarker::EOI EOI + * marker} and will probably not be useful. + * + * Please use @{link setExif()} instead if you intend to add Exif + * information to an image as that function will know the right + * place to insert the data. + * + * @param PelJpegMarker the marker identifying the new section. + * + * @param PelJpegContent the content of the new section. + */ + function appendSection($marker, PelJpegContent $content) { + $this->sections[] = array($marker, $content); + } + + + /** + * Insert a new section. + * + * Please use @{link setExif()} instead if you intend to add Exif + * information to an image as that function will know the right + * place to insert the data. + * + * @param PelJpegMarker the marker for the new section. + * + * @param PelJpegContent the content of the new section. + * + * @param int the offset where the new section will be inserted --- + * use 0 to insert it at the very beginning, use 1 to insert it + * between sections 1 and 2, etc. + */ + function insertSection($marker, PelJpegContent $content, $offset) { + array_splice($this->sections, $offset, 0, array(array($marker, $content))); + } + + + /** + * Get a section corresponding to a particular marker. + * + * Please use the {@link getExif()} if you just need the Exif data. + * + * This will search through the sections of this JPEG object, + * looking for a section identified with the specified {@link + * PelJpegMarker marker}. The {@link PelJpegContent content} will + * then be returned. The optional argument can be used to skip over + * some of the sections. So if one is looking for the, say, third + * {@link PelJpegMarker::DHT DHT} section one would do: + * + * + * $dht3 = $jpeg->getSection(PelJpegMarker::DHT, 2); + * + * + * @param PelJpegMarker the marker identifying the section. + * + * @param int the number of sections to be skipped. This must be a + * non-negative integer. + * + * @return PelJpegContent the content found, or null if there is no + * content available. + */ + function getSection($marker, $skip = 0) { + foreach ($this->sections as $s) { + if ($s[0] == $marker) + if ($skip > 0) + $skip--; + else + return $s[1]; + } + + return null; + } + + + /** + * Get all sections. + * + * @return array an array of ({@link PelJpegMarker}, {@link + * PelJpegContent}) pairs. Each pair is an array with the {@link + * PelJpegMarker} as the first element and the {@link + * PelJpegContent} as the second element, so the return type is an + * array of arrays. + * + * So to loop through all the sections in a given JPEG image do + * this: + * + * + * foreach ($jpeg->getSections() as $section) { + * $marker = $section[0]; + * $content = $section[1]; + * // Use $marker and $content here. + * } + * + * + * instead of this: + * + * + * foreach ($jpeg->getSections() as $marker => $content) { + * // Does not work the way you would think... + * } + * + * + * The problem is that there could be several sections with the same + * marker, and thus a simple associative array does not suffice. + */ + function getSections() { + return $this->sections; + } + + + /** + * Turn this JPEG object into bytes. + * + * The bytes returned by this method is ready to be stored in a file + * as a valid JPEG image. + * + * @return string bytes representing this JPEG object, including all + * its sections and their associated data. + */ + function getBytes() { + $bytes = ''; + + foreach ($this->sections as $section) { + $m = $section[0]; + $c = $section[1]; + + /* Write the marker */ + $bytes .= "\xFF" . PelJpegMarker::getBytes($m); + /* Skip over empty markers. */ + if ($m == PelJpegMarker::SOI || $m == PelJpegMarker::EOI) + continue; + + $data = $c->getBytes(); + $size = strlen($data) + 2; + + $bytes .= PelConvert::shortToBytes($size, PelConvert::BIG_ENDIAN); + $bytes .= $data; + + /* In case of SOS, we need to write the JPEG data. */ + if ($m == PelJpegMarker::SOS) + $bytes .= $this->jpeg_data->getBytes(); + } + + return $bytes; + + } + + + /** + * Make a string representation of this JPEG object. + * + * This is mainly usefull for debugging. It will show the structure + * of the image, and its sections. + * + * @return string debugging information about this JPEG object. + */ + function __toString() { + $str = Pel::tra("Dumping JPEG data...\n"); + for ($i = 0; $i < count($this->sections); $i++) { + $m = $this->sections[$i][0]; + $c = $this->sections[$i][1]; + $str .= Pel::fmt("Section %d (marker 0x%02X - %s):\n", + $i, $m, PelJpegMarker::getName($m)); + $str .= Pel::fmt(" Description: %s\n", + PelJpegMarker::getDescription($m)); + + if ($m == PelJpegMarker::SOI || + $m == PelJpegMarker::EOI) + continue; + + if ($c instanceof PelExif) { + $str .= Pel::tra(" Content : Exif data\n"); + $str .= $c->__toString() . "\n"; + } elseif ($c instanceof PelJpegComment) { + $str .= Pel::fmt(" Content : %s\n", $c->getValue()); + } else { + $str .= Pel::tra(" Content : Unknown\n"); + } + } + + return $str; + } + + + /** + * Test data to see if it could be a valid JPEG image. + * + * The function will only look at the first few bytes of the data, + * and try to determine if it could be a valid JPEG image based on + * those bytes. This means that the check is more like a heuristic + * than a rigorous check. + * + * @param PelDataWindow the bytes that will be checked. + * + * @return boolean true if the bytes look like the beginning of a + * JPEG image, false otherwise. + * + * @see PelTiff::isValid() + */ + static function isValid(PelDataWindow $d) { + /* JPEG data is stored in big-endian format. */ + $d->setByteOrder(PelConvert::BIG_ENDIAN); + + for ($i = 0; $i < 7; $i++) + if ($d->getByte($i) != 0xFF) + break; + + return $d->getByte($i) == PelJpegMarker::SOI; + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelJpegComment.php b/modules/autorotate/lib/pel/PelJpegComment.php new file mode 100644 index 00000000..feb62c30 --- /dev/null +++ b/modules/autorotate/lib/pel/PelJpegComment.php @@ -0,0 +1,121 @@ + + * @version $Revision: 397 $ + * @date $Date: 2005-10-24 00:40:26 +0200 (Mon, 24 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelJpegContent.php'); +/**#@-*/ + + +/** + * Class representing JPEG comments. + * + * @author Martin Geisler + * @package PEL + */ +class PelJpegComment extends PelJpegContent { + + /** + * The comment. + * + * @var string + */ + private $comment = ''; + + /** + * Construct a new JPEG comment. + * + * The new comment will contain the string given. + */ + function __construct($comment = '') { + $this->comment = $comment; + } + + + /** + * Load and parse data. + * + * This will load the comment from the data window passed. + */ + function load(PelDataWindow $d) { + $this->comment = $d->getBytes(); + } + + + /** + * Update the value with a new comment. + * + * Any old comment will be overwritten. + * + * @param string the new comment. + */ + function setValue($comment) { + $this->comment = $comment; + } + + + /** + * Get the comment. + * + * @return string the comment. + */ + function getValue() { + return $this->comment; + } + + + /** + * Turn this comment into bytes. + * + * @return string bytes representing this comment. + */ + function getBytes() { + $this->comment; + } + + + /** + * Return a string representation of this object. + * + * @return string the same as {@link getValue()}. + */ + function __toString() { + return $this->getValue(); + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelJpegContent.php b/modules/autorotate/lib/pel/PelJpegContent.php new file mode 100644 index 00000000..93eea2ae --- /dev/null +++ b/modules/autorotate/lib/pel/PelJpegContent.php @@ -0,0 +1,82 @@ + + * @version $Revision: 380 $ + * @date $Date: 2005-10-03 14:01:28 +0200 (Mon, 03 Oct 2005) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelDataWindow.php'); +/**#@-*/ + + +/** + * Class representing content in a JPEG file. + * + * A JPEG file consists of a sequence of each of which has an + * associated {@link PelJpegMarker marker} and some content. This + * class represents the content, and this basic type is just a simple + * holder of such content, represented by a {@link PelDataWindow} + * object. The {@link PelExif} class is an example of more + * specialized JPEG content. + * + * @author Martin Geisler + * @package PEL + */ +class PelJpegContent { + private $data = null; + + /** + * Make a new piece of JPEG content. + * + * @param PelDataWindow the content. + */ + function __construct(PelDataWindow $data) { + $this->data = $data; + } + + + /** + * Return the bytes of the content. + * + * @return string bytes representing this JPEG content. These bytes + * will match the bytes given to {@link __construct the + * constructor}. + */ + function getBytes() { + return $this->data->getBytes(); + } + +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelJpegMarker.php b/modules/autorotate/lib/pel/PelJpegMarker.php new file mode 100644 index 00000000..377b279b --- /dev/null +++ b/modules/autorotate/lib/pel/PelJpegMarker.php @@ -0,0 +1,435 @@ + + * @version $Revision: 432 $ + * @date $Date: 2006-09-06 00:13:00 +0200 (Wed, 06 Sep 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('Pel.php'); +/**#@-*/ + + +/** + * Class with static methods for JPEG markers. + * + * This class defines the constants to be used whenever one refers to + * a JPEG marker. All the methods defined are static, and they all + * operate on one argument which should be one of the class constants. + * They will all be denoted by PelJpegMarker in the documentation. + * + * @author Martin Geisler + * @package PEL + */ +class PelJpegMarker { + + /** Encoding (baseline) */ + const SOF0 = 0xC0; + /** Encoding (extended sequential) */ + const SOF1 = 0xC1; + /** Encoding (progressive) */ + const SOF2 = 0xC2; + /** Encoding (lossless) */ + const SOF3 = 0xC3; + /** Define Huffman table */ + const DHT = 0xC4; + /** Encoding (differential sequential) */ + const SOF5 = 0xC5; + /** Encoding (differential progressive) */ + const SOF6 = 0xC6; + /** Encoding (differential lossless) */ + const SOF7 = 0xC7; + /** Extension */ + const JPG = 0xC8; + /** Encoding (extended sequential, arithmetic) */ + const SOF9 = 0xC9; + /** Encoding (progressive, arithmetic) */ + const SOF10 = 0xCA; + /** Encoding (lossless, arithmetic) */ + const SOF11 = 0xCB; + /** Define arithmetic coding conditioning */ + const DAC = 0xCC; + /** Encoding (differential sequential, arithmetic) */ + const SOF13 = 0xCD; + /** Encoding (differential progressive, arithmetic) */ + const SOF14 = 0xCE; + /** Encoding (differential lossless, arithmetic) */ + const SOF15 = 0xCF; + /** Restart 0 */ + const RST0 = 0xD0; + /** Restart 1 */ + const RST1 = 0xD1; + /** Restart 2 */ + const RST2 = 0xD2; + /** Restart 3 */ + const RST3 = 0xD3; + /** Restart 4 */ + const RST4 = 0xD4; + /** Restart 5 */ + const RST5 = 0xD5; + /** Restart 6 */ + const RST6 = 0xD6; + /** Restart 7 */ + const RST7 = 0xD7; + /** Start of image */ + const SOI = 0xD8; + /** End of image */ + const EOI = 0xD9; + /** Start of scan */ + const SOS = 0xDA; + /** Define quantization table */ + const DQT = 0xDB; + /** Define number of lines */ + const DNL = 0xDC; + /** Define restart interval */ + const DRI = 0xDD; + /** Define hierarchical progression */ + const DHP = 0xDE; + /** Expand reference component */ + const EXP = 0xDF; + /** Application segment 0 */ + const APP0 = 0xE0; + /** + * Application segment 1 + * + * When a JPEG image contains Exif data, the data will normally be + * stored in this section and a call to {@link PelJpeg::getExif()} + * will return a {@link PelExif} object representing it. + */ + const APP1 = 0xE1; + /** Application segment 2 */ + const APP2 = 0xE2; + /** Application segment 3 */ + const APP3 = 0xE3; + /** Application segment 4 */ + const APP4 = 0xE4; + /** Application segment 5 */ + const APP5 = 0xE5; + /** Application segment 6 */ + const APP6 = 0xE6; + /** Application segment 7 */ + const APP7 = 0xE7; + /** Application segment 8 */ + const APP8 = 0xE8; + /** Application segment 9 */ + const APP9 = 0xE9; + /** Application segment 10 */ + const APP10 = 0xEA; + /** Application segment 11 */ + const APP11 = 0xEB; + /** Application segment 12 */ + const APP12 = 0xEC; + /** Application segment 13 */ + const APP13 = 0xED; + /** Application segment 14 */ + const APP14 = 0xEE; + /** Application segment 15 */ + const APP15 = 0xEF; + /** Extension 0 */ + const JPG0 = 0xF0; + /** Extension 1 */ + const JPG1 = 0xF1; + /** Extension 2 */ + const JPG2 = 0xF2; + /** Extension 3 */ + const JPG3 = 0xF3; + /** Extension 4 */ + const JPG4 = 0xF4; + /** Extension 5 */ + const JPG5 = 0xF5; + /** Extension 6 */ + const JPG6 = 0xF6; + /** Extension 7 */ + const JPG7 = 0xF7; + /** Extension 8 */ + const JPG8 = 0xF8; + /** Extension 9 */ + const JPG9 = 0xF9; + /** Extension 10 */ + const JPG10 = 0xFA; + /** Extension 11 */ + const JPG11 = 0xFB; + /** Extension 12 */ + const JPG12 = 0xFC; + /** Extension 13 */ + const JPG13 = 0xFD; + /** Comment */ + const COM = 0xFE; + + /** + * Check if a byte is a valid JPEG marker. + * + * @param PelJpegMarker the byte that will be checked. + * + * @return boolean if the byte is recognized true is returned, + * otherwise false will be returned. + */ + static function isValid($m) { + return ($m >= self::SOF0 && $m <= self::COM); + } + + /** + * Turn a JPEG marker into bytes. + * + * @param PelJpegMarker the marker. + * + * @return string the marker as a string. This will be a string + * with just a single byte since all JPEG markers are simply single + * bytes. + */ + static function getBytes($m) { + return chr($m); + } + + /** + * Return the short name for a marker. + * + * @param PelJpegMarker the marker. + * + * @return string the name of the marker, e.g., 'SOI' for the Start + * of Image marker. + */ + static function getName($m) { + switch ($m) { + case self::SOF0: return 'SOF0'; + case self::SOF1: return 'SOF1'; + case self::SOF2: return 'SOF2'; + case self::SOF3: return 'SOF3'; + case self::SOF5: return 'SOF5'; + case self::SOF6: return 'SOF6'; + case self::SOF7: return 'SOF7'; + case self::SOF9: return 'SOF9'; + case self::SOF10: return 'SOF10'; + case self::SOF11: return 'SOF11'; + case self::SOF13: return 'SOF13'; + case self::SOF14: return 'SOF14'; + case self::SOF15: return 'SOF15'; + case self::SOI: return 'SOI'; + case self::EOI: return 'EOI'; + case self::SOS: return 'SOS'; + case self::COM: return 'COM'; + case self::DHT: return 'DHT'; + case self::JPG: return 'JPG'; + case self::DAC: return 'DAC'; + case self::RST0: return 'RST0'; + case self::RST1: return 'RST1'; + case self::RST2: return 'RST2'; + case self::RST3: return 'RST3'; + case self::RST4: return 'RST4'; + case self::RST5: return 'RST5'; + case self::RST6: return 'RST6'; + case self::RST7: return 'RST7'; + case self::DQT: return 'DQT'; + case self::DNL: return 'DNL'; + case self::DRI: return 'DRI'; + case self::DHP: return 'DHP'; + case self::EXP: return 'EXP'; + case self::APP0: return 'APP0'; + case self::APP1: return 'APP1'; + case self::APP2: return 'APP2'; + case self::APP3: return 'APP3'; + case self::APP4: return 'APP4'; + case self::APP5: return 'APP5'; + case self::APP6: return 'APP6'; + case self::APP7: return 'APP7'; + case self::APP8: return 'APP8'; + case self::APP9: return 'APP9'; + case self::APP10: return 'APP10'; + case self::APP11: return 'APP11'; + case self::APP12: return 'APP12'; + case self::APP13: return 'APP13'; + case self::APP14: return 'APP14'; + case self::APP15: return 'APP15'; + case self::JPG0: return 'JPG0'; + case self::JPG1: return 'JPG1'; + case self::JPG2: return 'JPG2'; + case self::JPG3: return 'JPG3'; + case self::JPG4: return 'JPG4'; + case self::JPG5: return 'JPG5'; + case self::JPG6: return 'JPG6'; + case self::JPG7: return 'JPG7'; + case self::JPG8: return 'JPG8'; + case self::JPG9: return 'JPG9'; + case self::JPG10: return 'JPG10'; + case self::JPG11: return 'JPG11'; + case self::JPG12: return 'JPG12'; + case self::JPG13: return 'JPG13'; + case self::COM: return 'COM'; + default: return Pel::fmt('Unknown marker: 0x%02X', $m); + } + } + + /** + * Returns a description of a JPEG marker. + * + * @param PelJpegMarker the marker. + * + * @return string the description of the marker. + */ + static function getDescription($m) { + switch ($m) { + case self::SOF0: + return Pel::tra('Encoding (baseline)'); + case self::SOF1: + return Pel::tra('Encoding (extended sequential)'); + case self::SOF2: + return Pel::tra('Encoding (progressive)'); + case self::SOF3: + return Pel::tra('Encoding (lossless)'); + case self::SOF5: + return Pel::tra('Encoding (differential sequential)'); + case self::SOF6: + return Pel::tra('Encoding (differential progressive)'); + case self::SOF7: + return Pel::tra('Encoding (differential lossless)'); + case self::SOF9: + return Pel::tra('Encoding (extended sequential, arithmetic)'); + case self::SOF10: + return Pel::tra('Encoding (progressive, arithmetic)'); + case self::SOF11: + return Pel::tra('Encoding (lossless, arithmetic)'); + case self::SOF13: + return Pel::tra('Encoding (differential sequential, arithmetic)'); + case self::SOF14: + return Pel::tra('Encoding (differential progressive, arithmetic)'); + case self::SOF15: + return Pel::tra('Encoding (differential lossless, arithmetic)'); + case self::SOI: + return Pel::tra('Start of image'); + case self::EOI: + return Pel::tra('End of image'); + case self::SOS: + return Pel::tra('Start of scan'); + case self::COM: + return Pel::tra('Comment'); + case self::DHT: + return Pel::tra('Define Huffman table'); + case self::JPG: + return Pel::tra('Extension'); + case self::DAC: + return Pel::tra('Define arithmetic coding conditioning'); + case self::RST0: + return Pel::fmt('Restart %d', 0); + case self::RST1: + return Pel::fmt('Restart %d', 1); + case self::RST2: + return Pel::fmt('Restart %d', 2); + case self::RST3: + return Pel::fmt('Restart %d', 3); + case self::RST4: + return Pel::fmt('Restart %d', 4); + case self::RST5: + return Pel::fmt('Restart %d', 5); + case self::RST6: + return Pel::fmt('Restart %d', 6); + case self::RST7: + return Pel::fmt('Restart %d', 7); + case self::DQT: + return Pel::tra('Define quantization table'); + case self::DNL: + return Pel::tra('Define number of lines'); + case self::DRI: + return Pel::tra('Define restart interval'); + case self::DHP: + return Pel::tra('Define hierarchical progression'); + case self::EXP: + return Pel::tra('Expand reference component'); + case self::APP0: + return Pel::fmt('Application segment %d', 0); + case self::APP1: + return Pel::fmt('Application segment %d', 1); + case self::APP2: + return Pel::fmt('Application segment %d', 2); + case self::APP3: + return Pel::fmt('Application segment %d', 3); + case self::APP4: + return Pel::fmt('Application segment %d', 4); + case self::APP5: + return Pel::fmt('Application segment %d', 5); + case self::APP6: + return Pel::fmt('Application segment %d', 6); + case self::APP7: + return Pel::fmt('Application segment %d', 7); + case self::APP8: + return Pel::fmt('Application segment %d', 8); + case self::APP9: + return Pel::fmt('Application segment %d', 9); + case self::APP10: + return Pel::fmt('Application segment %d', 10); + case self::APP11: + return Pel::fmt('Application segment %d', 11); + case self::APP12: + return Pel::fmt('Application segment %d', 12); + case self::APP13: + return Pel::fmt('Application segment %d', 13); + case self::APP14: + return Pel::fmt('Application segment %d', 14); + case self::APP15: + return Pel::fmt('Application segment %d', 15); + case self::JPG0: + return Pel::fmt('Extension %d', 0); + case self::JPG1: + return Pel::fmt('Extension %d', 1); + case self::JPG2: + return Pel::fmt('Extension %d', 2); + case self::JPG3: + return Pel::fmt('Extension %d', 3); + case self::JPG4: + return Pel::fmt('Extension %d', 4); + case self::JPG5: + return Pel::fmt('Extension %d', 5); + case self::JPG6: + return Pel::fmt('Extension %d', 6); + case self::JPG7: + return Pel::fmt('Extension %d', 7); + case self::JPG8: + return Pel::fmt('Extension %d', 8); + case self::JPG9: + return Pel::fmt('Extension %d', 9); + case self::JPG10: + return Pel::fmt('Extension %d', 10); + case self::JPG11: + return Pel::fmt('Extension %d', 11); + case self::JPG12: + return Pel::fmt('Extension %d', 12); + case self::JPG13: + return Pel::fmt('Extension %d', 13); + case self::COM: + return Pel::tra('Comment'); + default: + return Pel::fmt('Unknown marker: 0x%02X', $m); + } + } +} + +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelTag.php b/modules/autorotate/lib/pel/PelTag.php new file mode 100644 index 00000000..5000811e --- /dev/null +++ b/modules/autorotate/lib/pel/PelTag.php @@ -0,0 +1,1969 @@ + + * @version $Revision: 472 $ + * @date $Date: 2006-11-19 21:29:14 +0100 (Sun, 19 Nov 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('Pel.php'); +require_once('PelIfd.php'); +/**#@-*/ + + +/** + * Class with static methods for Exif tags. + * + * This class defines the constants that represents the Exif tags + * known to PEL. They are supposed to be used whenever one needs to + * specify an Exif tag, and they will be denoted by the pseudo-type + * {@link PelTag} throughout the documentation. + * + * Please note that the constrains on the format and number of + * components given here are advisory only. To follow the Exif + * specification one should obey them, but there is nothing that + * prevents you from creating an {@link IMAGE_LENGTH} entry with two + * or more components, even though the standard says that there should + * be exactly one component. + * + * All the methods in this class are static and should be called with + * the Exif tag on which they should operate. + * + * @author Martin Geisler + * @package PEL + */ +class PelTag { + + /** + * Interoperability index. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 4. + */ + const INTEROPERABILITY_INDEX = 0x0001; + + /** + * Interoperability version. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: 4. + */ + const INTEROPERABILITY_VERSION = 0x0002; + + /** + * Image width. + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: 1. + */ + const IMAGE_WIDTH = 0x0100; + + /** + * Image length. + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: 1. + */ + const IMAGE_LENGTH = 0x0101; + + /** + * Number of bits per component. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 3. + */ + const BITS_PER_SAMPLE = 0x0102; + + /** + * Compression scheme. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const COMPRESSION = 0x0103; + + /** + * Pixel composition. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const PHOTOMETRIC_INTERPRETATION = 0x0106; + + /** + * Fill Orde + * + * Format: Unknown. + * + * Components: Unknown. + */ + const FILL_ORDER = 0x010A; + + /** + * Document Name + * + * Format: Unknown. + * + * Components: Unknown. + */ + const DOCUMENT_NAME = 0x010D; + + /** + * Image Description + * + * Format: {@link PelEntryAscii}. + * + * Components: any number. + */ + const IMAGE_DESCRIPTION = 0x010E; + + /** + * Manufacturer + * + * Format: {@link PelEntryAscii}. + * + * Components: any number. + */ + const MAKE = 0x010F; + + /** + * Model + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const MODEL = 0x0110; + + /** + * Strip Offsets + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: any number. + */ + const STRIP_OFFSETS = 0x0111; + + /** + * Orientation of image. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const ORIENTATION = 0x0112; + + /** + * Number of components. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SAMPLES_PER_PIXEL = 0x0115; + + /** + * Rows per Strip + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: 1. + */ + const ROWS_PER_STRIP = 0x0116; + + /** + * Strip Byte Count + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: any number. + */ + const STRIP_BYTE_COUNTS = 0x0117; + + /** + * Image resolution in width direction. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const X_RESOLUTION = 0x011A; + + /** + * Image resolution in height direction. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const Y_RESOLUTION = 0x011B; + + /** + * Image data arrangement. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const PLANAR_CONFIGURATION = 0x011C; + + /** + * Unit of X and Y resolution. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const RESOLUTION_UNIT = 0x0128; + + /** + * Transfer function. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 3. + */ + const TRANSFER_FUNCTION = 0x012D; + + /** + * Software used. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const SOFTWARE = 0x0131; + + /** + * File change date and time. + * + * Format: {@link PelFormat::ASCII}, modelled by the {@link + * PelEntryTime} class. + * + * Components: 20. + */ + const DATE_TIME = 0x0132; + + /** + * Person who created the image. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const ARTIST = 0x013B; + + /** + * White point chromaticity. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 2. + */ + const WHITE_POINT = 0x013E; + + /** + * Chromaticities of primaries. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 6. + */ + const PRIMARY_CHROMATICITIES = 0x013F; + + /** + * Transfer Range + * + * Format: Unknown. + * + * Components: Unknown. + */ + const TRANSFER_RANGE = 0x0156; + + /** + * JPEGProc + * + * Format: Unknown. + * + * Components: Unknown. + */ + const JPEG_PROC = 0x0200; + + /** + * Offset to JPEG SOI. + * + * Format: {@link PelFormat::LONG}. + * + * Components: 1. + */ + const JPEG_INTERCHANGE_FORMAT = 0x0201; + + /** + * Bytes of JPEG data. + * + * Format: {@link PelFormat::LONG}. + * + * Components: 1. + */ + const JPEG_INTERCHANGE_FORMAT_LENGTH = 0x0202; + + /** + * Color space transformation matrix coefficients. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 3. + */ + const YCBCR_COEFFICIENTS = 0x0211; + + /** + * Subsampling ratio of Y to C. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 2. + */ + const YCBCR_SUB_SAMPLING = 0x0212; + + /** + * Y and C positioning. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const YCBCR_POSITIONING = 0x0213; + + /** + * Pair of black and white reference values. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 6. + */ + const REFERENCE_BLACK_WHITE = 0x0214; + + /** + * Related Image File Format + * + * Format: Unknown. + * + * Components: Unknown. + */ + const RELATED_IMAGE_FILE_FORMAT = 0x1000; + + /** + * Related Image Width + * + * Format: Unknown, probably {@link PelFormat::SHORT}? + * + * Components: Unknown, probably 1. + */ + const RELATED_IMAGE_WIDTH = 0x1001; + + /** Related Image Length + * + * Format: Unknown, probably {@link PelFormat::SHORT}? + * + * Components: Unknown, probably 1. + */ + const RELATED_IMAGE_LENGTH = 0x1002; + + /** + * CFA Repeat Pattern Dim. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 2. + */ + const CFA_REPEAT_PATTERN_DIM = 0x828D; + + /** + * Battery level. + * + * Format: Unknown. + * + * Components: Unknown. + */ + const BATTERY_LEVEL = 0x828F; + + /** + * Copyright holder. + * + * Format: {@link PelFormat::ASCII}, modelled by the {@link + * PelEntryCopyright} class. + * + * Components: any number. + */ + const COPYRIGHT = 0x8298; + + /** + * Exposure Time + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const EXPOSURE_TIME = 0x829A; + + /** + * FNumber + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const FNUMBER = 0x829D; + + /** + * IPTC/NAA + * + * Format: {@link PelFormat::LONG}. + * + * Components: any number. + */ + const IPTC_NAA = 0x83BB; + + /** + * Exif IFD Pointer + * + * Format: {@link PelFormat::LONG}. + * + * Components: 1. + */ + const EXIF_IFD_POINTER = 0x8769; + + /** + * Inter Color Profile + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: any number. + */ + const INTER_COLOR_PROFILE = 0x8773; + + /** + * Exposure Program + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const EXPOSURE_PROGRAM = 0x8822; + + /** + * Spectral Sensitivity + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const SPECTRAL_SENSITIVITY = 0x8824; + + /** + * GPS Info IFD Pointer + * + * Format: {@link PelFormat::LONG}. + * + * Components: 1. + */ + const GPS_INFO_IFD_POINTER = 0x8825; + + /** + * ISO Speed Ratings + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 2. + */ + const ISO_SPEED_RATINGS = 0x8827; + + /** + * OECF + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: any number. + */ + const OECF = 0x8828; + + /** + * Exif version. + * + * Format: {@link PelFormat::UNDEFINED}, modelled by the {@link + * PelEntryVersion} class. + * + * Components: 4. + */ + const EXIF_VERSION = 0x9000; + + /** + * Date and time of original data generation. + * + * Format: {@link PelFormat::ASCII}, modelled by the {@link + * PelEntryTime} class. + * + * Components: 20. + */ + const DATE_TIME_ORIGINAL = 0x9003; + + /** + * Date and time of digital data generation. + * + * Format: {@link PelFormat::ASCII}, modelled by the {@link + * PelEntryTime} class. + * + * Components: 20. + */ + const DATE_TIME_DIGITIZED = 0x9004; + + /** + * Meaning of each component. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: 4. + */ + const COMPONENTS_CONFIGURATION = 0x9101; + + /** + * Image compression mode. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const COMPRESSED_BITS_PER_PIXEL = 0x9102; + + /** + * Shutter speed + * + * Format: {@link PelFormat::SRATIONAL}. + * + * Components: 1. + */ + const SHUTTER_SPEED_VALUE = 0x9201; + + /** + * Aperture + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const APERTURE_VALUE = 0x9202; + + /** + * Brightness + * + * Format: {@link PelFormat::SRATIONAL}. + * + * Components: 1. + */ + const BRIGHTNESS_VALUE = 0x9203; + + /** + * Exposure Bias + * + * Format: {@link PelFormat::SRATIONAL}. + * + * Components: 1. + */ + const EXPOSURE_BIAS_VALUE = 0x9204; + + /** + * Max Aperture Value + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const MAX_APERTURE_VALUE = 0x9205; + + /** + * Subject Distance + * + * Format: {@link PelFormat::SRATIONAL}. + * + * Components: 1. + */ + const SUBJECT_DISTANCE = 0x9206; + + /** + * Metering Mode + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const METERING_MODE = 0x9207; + + /** + * Light Source + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const LIGHT_SOURCE = 0x9208; + + /** + * Flash + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const FLASH = 0x9209; + + /** + * Focal Length + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const FOCAL_LENGTH = 0x920A; + + /** + * Subject Area + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 4. + */ + const SUBJECT_AREA = 0x9214; + + /** + * Maker Note + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: any number. + */ + const MAKER_NOTE = 0x927C; + + /** + * User Comment + * + * Format: {@link PelFormat::UNDEFINED}, modelled by the {@link + * PelEntryUserComment} class. + * + * Components: any number. + */ + const USER_COMMENT = 0x9286; + + /** + * SubSec Time + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const SUB_SEC_TIME = 0x9290; + + /** + * SubSec Time Original + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const SUB_SEC_TIME_ORIGINAL = 0x9291; + + /** + * SubSec Time Digitized + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const SUB_SEC_TIME_DIGITIZED = 0x9292; + + /** + * Windows XP Title + * + * Format: {@link PelFormat::BYTE}. + * + * Components: any number. + */ + const XP_TITLE = 0x9C9B; + + + /** + * Windows XP Comment + * + * Format: {@link PelFormat::BYTE}. + * + * Components: any number. + */ + const XP_COMMENT = 0x9C9C; + + + /** + * Windows XP Author + * + * Format: {@link PelFormat::BYTE}. + * + * Components: any number. + */ + const XP_AUTHOR = 0x9C9D; + + + /** + * Windows XP Keywords + * + * Format: {@link PelFormat::BYTE}. + * + * Components: any number. + */ + const XP_KEYWORDS = 0x9C9E; + + + /** + * Windows XP Subject + * + * Format: {@link PelFormat::BYTE}. + * + * Components: any number. + */ + const XP_SUBJECT = 0x9C9F; + + + /** + * Supported Flashpix version + * + * Format: {@link PelFormat::UNDEFINED}, modelled by the {@link + * PelEntryVersion} class. + * + * Components: 4. + */ + const FLASH_PIX_VERSION = 0xA000; + + /** + * Color space information. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const COLOR_SPACE = 0xA001; + + /** + * Valid image width. + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: 1. + */ + const PIXEL_X_DIMENSION = 0xA002; + + /** + * Valid image height. + * + * Format: {@link PelFormat::SHORT} or {@link PelFormat::LONG}. + * + * Components: 1. + */ + const PIXEL_Y_DIMENSION = 0xA003; + + /** + * Related audio file. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: any number. + */ + const RELATED_SOUND_FILE = 0xA004; + + /** + * Interoperability IFD Pointer + * + * Format: {@link PelFormat::LONG}. + * + * Components: 1. + */ + const INTEROPERABILITY_IFD_POINTER = 0xA005; + + /** + * Flash energy. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const FLASH_ENERGY = 0xA20B; + + /** + * Spatial frequency response. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: any number. + */ + const SPATIAL_FREQUENCY_RESPONSE = 0xA20C; + + /** + * Focal plane X resolution. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const FOCAL_PLANE_X_RESOLUTION = 0xA20E; + + /** + * Focal plane Y resolution. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const FOCAL_PLANE_Y_RESOLUTION = 0xA20F; + + /** + * Focal plane resolution unit. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const FOCAL_PLANE_RESOLUTION_UNIT = 0xA210; + + /** + * Subject location. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SUBJECT_LOCATION = 0xA214; + + /** + * Exposure index. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const EXPOSURE_INDEX = 0xA215; + + /** + * Sensing method. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SENSING_METHOD = 0xA217; + + /** + * File source. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: 1. + */ + const FILE_SOURCE = 0xA300; + + /** + * Scene type. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: 1. + */ + const SCENE_TYPE = 0xA301; + + /** + * CFA pattern. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: any number. + */ + const CFA_PATTERN = 0xA302; + + /** + * Custom image processing. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const CUSTOM_RENDERED = 0xA401; + + /** + * Exposure mode. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const EXPOSURE_MODE = 0xA402; + + /** + * White balance. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const WHITE_BALANCE = 0xA403; + + /** + * Digital zoom ratio. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const DIGITAL_ZOOM_RATIO = 0xA404; + + /** + * Focal length in 35mm film. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const FOCAL_LENGTH_IN_35MM_FILM = 0xA405; + + /** + * Scene capture type. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SCENE_CAPTURE_TYPE = 0xA406; + + /** + * Gain control. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const GAIN_CONTROL = 0xA407; + + /** + * Contrast. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const CONTRAST = 0xA408; + + /** + * Saturation. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SATURATION = 0xA409; + + /** + * Sharpness. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SHARPNESS = 0xA40A; + + /** + * Device settings description. + * + * This tag indicates information on the picture-taking conditions + * of a particular camera model. The tag is used only to indicate + * the picture-taking conditions in the reader. + */ + const DEVICE_SETTING_DESCRIPTION = 0xA40B; + + /** + * Subject distance range. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const SUBJECT_DISTANCE_RANGE = 0xA40C; + + /** + * Image unique ID. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 32. + */ + const IMAGE_UNIQUE_ID = 0xA420; + + /** + * Gamma. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GAMMA = 0xA500; + + /** + * PrintIM + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: unknown. + */ + const PRINT_IM = 0xC4A5; + + /** + * GPS tag version. + * + * Format: {@link PelFormat::BYTE}. + * + * Components: 4. + */ + const GPS_VERSION_ID = 0x0000; + + /** + * North or South Latitude. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_LATITUDE_REF = 0x0001; + + /** + * Latitude. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 3. + */ + const GPS_LATITUDE = 0x0002; + + /** + * East or West Longitude. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_LONGITUDE_REF = 0x0003; + + /** + * Longitude. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 3. + */ + const GPS_LONGITUDE = 0x0004; + + /** + * Altitude reference. + * + * Format: {@link PelFormat::BYTE}. + * + * Components: 1. + */ + const GPS_ALTITUDE_REF = 0x0005; + + /** + * Altitude. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_ALTITUDE = 0x0006; + + /** + * GPS time (atomic clock). + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 3. + */ + const GPS_TIME_STAMP = 0x0007; + + /** + * GPS satellites used for measurement. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: Any. + */ + const GPS_SATELLITES = 0x0008; + + /** + * GPS receiver status. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_STATUS = 0x0009; + + /** + * GPS measurement mode. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_MEASURE_MODE = 0x000A; + + /** + * Measurement precision. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_DOP = 0x000B; + + /** + * Speed unit. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_SPEED_REF = 0x000C; + + /** + * Speed of GPS receiver. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_SPEED = 0x000D; + + /** + * Reference for direction of movement. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_TRACK_REF = 0x000E; + + /** + * Direction of movement. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_TRACK = 0x000F; + + /** + * Reference for direction of image. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_IMG_DIRECTION_REF = 0x0010; + + /** + * Direction of image. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_IMG_DIRECTION = 0x0011; + + /** + * Geodetic survey data used. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: Any. + */ + const GPS_MAP_DATUM = 0x0012; + + /** + * Reference for latitude of destination. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_DEST_LATITUDE_REF = 0x0013; + + /** + * Latitude of destination. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 3. + */ + const GPS_DEST_LATITUDE = 0x0014; + + /** + * Reference for longitude of destination. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_DEST_LONGITUDE_REF = 0x0015; + + /** + * Longitude of destination. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 3. + */ + const GPS_DEST_LONGITUDE = 0x0016; + + /** + * Reference for bearing of destination. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_DEST_BEARING_REF = 0x0017; + + /** + * Bearing of destination. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_DEST_BEARING = 0x0018; + + /** + * Reference for distance to destination. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 2. + */ + const GPS_DEST_DISTANCE_REF = 0x0019; + + /** + * Distance to destination. + * + * Format: {@link PelFormat::RATIONAL}. + * + * Components: 1. + */ + const GPS_DEST_DISTANCE = 0x001A; + + /** + * Name of GPS processing method. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: Any. + */ + const GPS_PROCESSING_METHOD = 0x001B; + + /** + * Name of GPS area. + * + * Format: {@link PelFormat::UNDEFINED}. + * + * Components: Any. + */ + const GPS_AREA_INFORMATION = 0x001C; + + /** + * GPS date. + * + * Format: {@link PelFormat::ASCII}. + * + * Components: 11. + */ + const GPS_DATE_STAMP = 0x001D; + + /** + * GPS differential correction. + * + * Format: {@link PelFormat::SHORT}. + * + * Components: 1. + */ + const GPS_DIFFERENTIAL = 0x001E; + + + /** + * Returns a short name for an Exif tag. + * + * @param int the IFD type of the tag, one of {@link PelIfd::IFD0}, + * {@link PelIfd::IFD1}, {@link PelIfd::EXIF}, {@link PelIfd::GPS}, + * or {@link PelIfd::INTEROPERABILITY}. + * + * @param PelTag the tag. + * + * @return string the short name of the tag, e.g., 'ImageWidth' for + * the {@link IMAGE_WIDTH} tag. If the tag is not known, the string + * 'Unknown:0xTTTT' will be returned where 'TTTT' is the hexadecimal + * representation of the tag. + */ + static function getName($type, $tag) { + + switch ($type) { + case PelIfd::IFD0: + case PelIfd::IFD1: + case PelIfd::EXIF: + case PelIfd::INTEROPERABILITY: + + switch ($tag) { + case self::INTEROPERABILITY_INDEX: + return 'InteroperabilityIndex'; + case self::INTEROPERABILITY_VERSION: + return 'InteroperabilityVersion'; + case self::IMAGE_WIDTH: + return 'ImageWidth'; + case self::IMAGE_LENGTH: + return 'ImageLength'; + case self::BITS_PER_SAMPLE: + return 'BitsPerSample'; + case self::COMPRESSION: + return 'Compression'; + case self::PHOTOMETRIC_INTERPRETATION: + return 'PhotometricInterpretation'; + case self::FILL_ORDER: + return 'FillOrder'; + case self::DOCUMENT_NAME: + return 'DocumentName'; + case self::IMAGE_DESCRIPTION: + return 'ImageDescription'; + case self::MAKE: + return 'Make'; + case self::MODEL: + return 'Model'; + case self::STRIP_OFFSETS: + return 'StripOffsets'; + case self::ORIENTATION: + return 'Orientation'; + case self::SAMPLES_PER_PIXEL: + return 'SamplesPerPixel'; + case self::ROWS_PER_STRIP: + return 'RowsPerStrip'; + case self::STRIP_BYTE_COUNTS: + return 'StripByteCounts'; + case self::X_RESOLUTION: + return 'XResolution'; + case self::Y_RESOLUTION: + return 'YResolution'; + case self::PLANAR_CONFIGURATION: + return 'PlanarConfiguration'; + case self::RESOLUTION_UNIT: + return 'ResolutionUnit'; + case self::TRANSFER_FUNCTION: + return 'TransferFunction'; + case self::SOFTWARE: + return 'Software'; + case self::DATE_TIME: + return 'DateTime'; + case self::ARTIST: + return 'Artist'; + case self::WHITE_POINT: + return 'WhitePoint'; + case self::PRIMARY_CHROMATICITIES: + return 'PrimaryChromaticities'; + case self::TRANSFER_RANGE: + return 'TransferRange'; + case self::JPEG_PROC: + return 'JPEGProc'; + case self::JPEG_INTERCHANGE_FORMAT: + return 'JPEGInterchangeFormat'; + case self::JPEG_INTERCHANGE_FORMAT_LENGTH: + return 'JPEGInterchangeFormatLength'; + case self::YCBCR_COEFFICIENTS: + return 'YCbCrCoefficients'; + case self::YCBCR_SUB_SAMPLING: + return 'YCbCrSubSampling'; + case self::YCBCR_POSITIONING: + return 'YCbCrPositioning'; + case self::REFERENCE_BLACK_WHITE: + return 'ReferenceBlackWhite'; + case self::RELATED_IMAGE_FILE_FORMAT: + return 'RelatedImageFileFormat'; + case self::RELATED_IMAGE_WIDTH: + return 'RelatedImageWidth'; + case self::RELATED_IMAGE_LENGTH: + return 'RelatedImageLength'; + case self::CFA_REPEAT_PATTERN_DIM: + return 'CFARepeatPatternDim'; + case self::CFA_PATTERN: + return 'CFAPattern'; + case self::BATTERY_LEVEL: + return 'BatteryLevel'; + case self::COPYRIGHT: + return 'Copyright'; + case self::EXPOSURE_TIME: + return 'ExposureTime'; + case self::FNUMBER: + return 'FNumber'; + case self::IPTC_NAA: + return 'IPTC/NAA'; + case self::EXIF_IFD_POINTER: + return 'ExifIFDPointer'; + case self::INTER_COLOR_PROFILE: + return 'InterColorProfile'; + case self::EXPOSURE_PROGRAM: + return 'ExposureProgram'; + case self::SPECTRAL_SENSITIVITY: + return 'SpectralSensitivity'; + case self::GPS_INFO_IFD_POINTER: + return 'GPSInfoIFDPointer'; + case self::ISO_SPEED_RATINGS: + return 'ISOSpeedRatings'; + case self::OECF: + return 'OECF'; + case self::EXIF_VERSION: + return 'ExifVersion'; + case self::DATE_TIME_ORIGINAL: + return 'DateTimeOriginal'; + case self::DATE_TIME_DIGITIZED: + return 'DateTimeDigitized'; + case self::COMPONENTS_CONFIGURATION: + return 'ComponentsConfiguration'; + case self::COMPRESSED_BITS_PER_PIXEL: + return 'CompressedBitsPerPixel'; + case self::SHUTTER_SPEED_VALUE: + return 'ShutterSpeedValue'; + case self::APERTURE_VALUE: + return 'ApertureValue'; + case self::BRIGHTNESS_VALUE: + return 'BrightnessValue'; + case self::EXPOSURE_BIAS_VALUE: + return 'ExposureBiasValue'; + case self::MAX_APERTURE_VALUE: + return 'MaxApertureValue'; + case self::SUBJECT_DISTANCE: + return 'SubjectDistance'; + case self::METERING_MODE: + return 'MeteringMode'; + case self::LIGHT_SOURCE: + return 'LightSource'; + case self::FLASH: + return 'Flash'; + case self::FOCAL_LENGTH: + return 'FocalLength'; + case self::MAKER_NOTE: + return 'MakerNote'; + case self::USER_COMMENT: + return 'UserComment'; + case self::SUB_SEC_TIME: + return 'SubSecTime'; + case self::SUB_SEC_TIME_ORIGINAL: + return 'SubSecTimeOriginal'; + case self::SUB_SEC_TIME_DIGITIZED: + return 'SubSecTimeDigitized'; + case self::XP_TITLE: + return 'WindowsXPTitle'; + case self::XP_COMMENT: + return 'WindowsXPComment'; + case self::XP_AUTHOR: + return 'WindowsXPAuthor'; + case self::XP_KEYWORDS: + return 'WindowsXPKeywords'; + case self::XP_SUBJECT: + return 'WindowsXPSubject'; + case self::FLASH_PIX_VERSION: + return 'FlashPixVersion'; + case self::COLOR_SPACE: + return 'ColorSpace'; + case self::PIXEL_X_DIMENSION: + return 'PixelXDimension'; + case self::PIXEL_Y_DIMENSION: + return 'PixelYDimension'; + case self::RELATED_SOUND_FILE: + return 'RelatedSoundFile'; + case self::INTEROPERABILITY_IFD_POINTER: + return 'InteroperabilityIFDPointer'; + case self::FLASH_ENERGY: + return 'FlashEnergy'; + case self::SPATIAL_FREQUENCY_RESPONSE: + return 'SpatialFrequencyResponse'; + case self::FOCAL_PLANE_X_RESOLUTION: + return 'FocalPlaneXResolution'; + case self::FOCAL_PLANE_Y_RESOLUTION: + return 'FocalPlaneYResolution'; + case self::FOCAL_PLANE_RESOLUTION_UNIT: + return 'FocalPlaneResolutionUnit'; + case self::SUBJECT_LOCATION: + return 'SubjectLocation'; + case self::EXPOSURE_INDEX: + return 'ExposureIndex'; + case self::SENSING_METHOD: + return 'SensingMethod'; + case self::FILE_SOURCE: + return 'FileSource'; + case self::SCENE_TYPE: + return 'SceneType'; + case self::SUBJECT_AREA: + return 'SubjectArea'; + case self::CUSTOM_RENDERED: + return 'CustomRendered'; + case self::EXPOSURE_MODE: + return 'ExposureMode'; + case self::WHITE_BALANCE: + return 'WhiteBalance'; + case self::DIGITAL_ZOOM_RATIO: + return 'DigitalZoomRatio'; + case self::FOCAL_LENGTH_IN_35MM_FILM: + return 'FocalLengthIn35mmFilm'; + case self::SCENE_CAPTURE_TYPE: + return 'SceneCaptureType'; + case self::GAIN_CONTROL: + return 'GainControl'; + case self::CONTRAST: + return 'Contrast'; + case self::SATURATION: + return 'Saturation'; + case self::SHARPNESS: + return 'Sharpness'; + case self::DEVICE_SETTING_DESCRIPTION: + return 'DeviceSettingDescription'; + case self::SUBJECT_DISTANCE_RANGE: + return 'SubjectDistanceRange'; + case self::IMAGE_UNIQUE_ID: + return 'ImageUniqueID'; + case self::GAMMA: + return 'Gamma'; + case self::PRINT_IM: + return 'PrintIM'; + } + + case PelIfd::GPS: + switch ($tag) { + case self::GPS_VERSION_ID: + return 'GPSVersionID'; + case self::GPS_LATITUDE_REF: + return 'GPSLatitudeRef'; + case self::GPS_LATITUDE: + return 'GPSLatitude'; + case self::GPS_LONGITUDE_REF: + return 'GPSLongitudeRef'; + case self::GPS_LONGITUDE: + return 'GPSLongitude'; + case self::GPS_ALTITUDE_REF: + return 'GPSAltitudeRef'; + case self::GPS_ALTITUDE: + return 'GPSAltitude'; + case self::GPS_TIME_STAMP: + return 'GPSTimeStamp'; + case self::GPS_SATELLITES: + return 'GPSSatellites'; + case self::GPS_STATUS: + return 'GPSStatus'; + case self::GPS_MEASURE_MODE: + return 'GPSMeasureMode'; + case self::GPS_DOP: + return 'GPSDOP'; + case self::GPS_SPEED_REF: + return 'GPSSpeedRef'; + case self::GPS_SPEED: + return 'GPSSpeed'; + case self::GPS_TRACK_REF: + return 'GPSTrackRef'; + case self::GPS_TRACK: + return 'GPSTrack'; + case self::GPS_IMG_DIRECTION_REF: + return 'GPSImgDirectionRef'; + case self::GPS_IMG_DIRECTION: + return 'GPSImgDirection'; + case self::GPS_MAP_DATUM: + return 'GPSMapDatum'; + case self::GPS_DEST_LATITUDE_REF: + return 'GPSDestLatitudeRef'; + case self::GPS_DEST_LATITUDE: + return 'GPSDestLatitude'; + case self::GPS_DEST_LONGITUDE_REF: + return 'GPSDestLongitudeRef'; + case self::GPS_DEST_LONGITUDE: + return 'GPSDestLongitude'; + case self::GPS_DEST_BEARING_REF: + return 'GPSDestBearingRef'; + case self::GPS_DEST_BEARING: + return 'GPSDestBearing'; + case self::GPS_DEST_DISTANCE_REF: + return 'GPSDestDistanceRef'; + case self::GPS_DEST_DISTANCE: + return 'GPSDestDistance'; + case self::GPS_PROCESSING_METHOD: + return 'GPSProcessingMethod'; + case self::GPS_AREA_INFORMATION: + return 'GPSAreaInformation'; + case self::GPS_DATE_STAMP: + return 'GPSDateStamp'; + case self::GPS_DIFFERENTIAL: + return 'GPSDifferential'; + } + + default: + return Pel::fmt('Unknown: 0x%04X', $tag); + } + } + + + /** + * Returns a title for an Exif tag. + * + * @param int the IFD type of the tag, one of {@link PelIfd::IFD0}, + * {@link PelIfd::IFD1}, {@link PelIfd::EXIF}, {@link PelIfd::GPS}, + * or {@link PelIfd::INTEROPERABILITY}. + * + * @param PelTag the tag. + * + * @return string the title of the tag, e.g., 'Image Width' for the + * {@link IMAGE_WIDTH} tag. If the tag isn't known, the string + * 'Unknown Tag: 0xTT' will be returned where 'TT' is the + * hexadecimal representation of the tag. + */ + function getTitle($type, $tag) { + + switch ($type) { + case PelIfd::IFD0: + case PelIfd::IFD1: + case PelIfd::EXIF: + case PelIfd::INTEROPERABILITY: + + switch ($tag) { + case self::INTEROPERABILITY_INDEX: + return Pel::tra('Interoperability Index'); + case self::INTEROPERABILITY_VERSION: + return Pel::tra('Interoperability Version'); + case self::IMAGE_WIDTH: + return Pel::tra('Image Width'); + case self::IMAGE_LENGTH: + return Pel::tra('Image Length'); + case self::BITS_PER_SAMPLE: + return Pel::tra('Bits per Sample'); + case self::COMPRESSION: + return Pel::tra('Compression'); + case self::PHOTOMETRIC_INTERPRETATION: + return Pel::tra('Photometric Interpretation'); + case self::FILL_ORDER: + return Pel::tra('Fill Order'); + case self::DOCUMENT_NAME: + return Pel::tra('Document Name'); + case self::IMAGE_DESCRIPTION: + return Pel::tra('Image Description'); + case self::MAKE: + return Pel::tra('Manufacturer'); + case self::MODEL: + return Pel::tra('Model'); + case self::STRIP_OFFSETS: + return Pel::tra('Strip Offsets'); + case self::ORIENTATION: + return Pel::tra('Orientation'); + case self::SAMPLES_PER_PIXEL: + return Pel::tra('Samples per Pixel'); + case self::ROWS_PER_STRIP: + return Pel::tra('Rows per Strip'); + case self::STRIP_BYTE_COUNTS: + return Pel::tra('Strip Byte Count'); + case self::X_RESOLUTION: + return Pel::tra('x-Resolution'); + case self::Y_RESOLUTION: + return Pel::tra('y-Resolution'); + case self::PLANAR_CONFIGURATION: + return Pel::tra('Planar Configuration'); + case self::RESOLUTION_UNIT: + return Pel::tra('Resolution Unit'); + case self::TRANSFER_FUNCTION: + return Pel::tra('Transfer Function'); + case self::SOFTWARE: + return Pel::tra('Software'); + case self::DATE_TIME: + return Pel::tra('Date and Time'); + case self::ARTIST: + return Pel::tra('Artist'); + case self::WHITE_POINT: + return Pel::tra('White Point'); + case self::PRIMARY_CHROMATICITIES: + return Pel::tra('Primary Chromaticities'); + case self::TRANSFER_RANGE: + return Pel::tra('Transfer Range'); + case self::JPEG_PROC: + return Pel::tra('JPEG Process'); + case self::JPEG_INTERCHANGE_FORMAT: + return Pel::tra('JPEG Interchange Format'); + case self::JPEG_INTERCHANGE_FORMAT_LENGTH: + return Pel::tra('JPEG Interchange Format Length'); + case self::YCBCR_COEFFICIENTS: + return Pel::tra('YCbCr Coefficients'); + case self::YCBCR_SUB_SAMPLING: + return Pel::tra('YCbCr Sub-Sampling'); + case self::YCBCR_POSITIONING: + return Pel::tra('YCbCr Positioning'); + case self::REFERENCE_BLACK_WHITE: + return Pel::tra('Reference Black/White'); + case self::RELATED_IMAGE_FILE_FORMAT: + return Pel::tra('Related Image File Format'); + case self::RELATED_IMAGE_WIDTH: + return Pel::tra('Related Image Width'); + case self::RELATED_IMAGE_LENGTH: + return Pel::tra('Related Image Length'); + case self::CFA_REPEAT_PATTERN_DIM: + return Pel::tra('CFA Repeat Pattern Dim'); + case self::CFA_PATTERN: + return Pel::tra('CFA Pattern'); + case self::BATTERY_LEVEL: + return Pel::tra('Battery Level'); + case self::COPYRIGHT: + return Pel::tra('Copyright'); + case self::EXPOSURE_TIME: + return Pel::tra('Exposure Time'); + case self::FNUMBER: + return Pel::tra('FNumber'); + case self::IPTC_NAA: + return Pel::tra('IPTC/NAA'); + case self::EXIF_IFD_POINTER: + return Pel::tra('Exif IFD Pointer'); + case self::INTER_COLOR_PROFILE: + return Pel::tra('Inter Color Profile'); + case self::EXPOSURE_PROGRAM: + return Pel::tra('Exposure Program'); + case self::SPECTRAL_SENSITIVITY: + return Pel::tra('Spectral Sensitivity'); + case self::GPS_INFO_IFD_POINTER: + return Pel::tra('GPS Info IFD Pointer'); + case self::ISO_SPEED_RATINGS: + return Pel::tra('ISO Speed Ratings'); + case self::OECF: + return Pel::tra('OECF'); + case self::EXIF_VERSION: + return Pel::tra('Exif Version'); + case self::DATE_TIME_ORIGINAL: + return Pel::tra('Date and Time (original)'); + case self::DATE_TIME_DIGITIZED: + return Pel::tra('Date and Time (digitized)'); + case self::COMPONENTS_CONFIGURATION: + return Pel::tra('Components Configuration'); + case self::COMPRESSED_BITS_PER_PIXEL: + return Pel::tra('Compressed Bits per Pixel'); + case self::SHUTTER_SPEED_VALUE: + return Pel::tra('Shutter speed'); + case self::APERTURE_VALUE: + return Pel::tra('Aperture'); + case self::BRIGHTNESS_VALUE: + return Pel::tra('Brightness'); + case self::EXPOSURE_BIAS_VALUE: + return Pel::tra('Exposure Bias'); + case self::MAX_APERTURE_VALUE: + return Pel::tra('Max Aperture Value'); + case self::SUBJECT_DISTANCE: + return Pel::tra('Subject Distance'); + case self::METERING_MODE: + return Pel::tra('Metering Mode'); + case self::LIGHT_SOURCE: + return Pel::tra('Light Source'); + case self::FLASH: + return Pel::tra('Flash'); + case self::FOCAL_LENGTH: + return Pel::tra('Focal Length'); + case self::MAKER_NOTE: + return Pel::tra('Maker Note'); + case self::USER_COMMENT: + return Pel::tra('User Comment'); + case self::SUB_SEC_TIME: + return Pel::tra('SubSec Time'); + case self::SUB_SEC_TIME_ORIGINAL: + return Pel::tra('SubSec Time Original'); + case self::SUB_SEC_TIME_DIGITIZED: + return Pel::tra('SubSec Time Digitized'); + case self::XP_TITLE: + return 'Windows XP Title'; + case self::XP_COMMENT: + return 'Windows XP Comment'; + case self::XP_AUTHOR: + return 'Windows XP Author'; + case self::XP_KEYWORDS: + return 'Windows XP Keywords'; + case self::XP_SUBJECT: + return 'Windows XP Subject'; + case self::FLASH_PIX_VERSION: + return Pel::tra('FlashPix Version'); + case self::COLOR_SPACE: + return Pel::tra('Color Space'); + case self::PIXEL_X_DIMENSION: + return Pel::tra('Pixel x-Dimension'); + case self::PIXEL_Y_DIMENSION: + return Pel::tra('Pixel y-Dimension'); + case self::RELATED_SOUND_FILE: + return Pel::tra('Related Sound File'); + case self::INTEROPERABILITY_IFD_POINTER: + return Pel::tra('Interoperability IFD Pointer'); + case self::FLASH_ENERGY: + return Pel::tra('Flash Energy'); + case self::SPATIAL_FREQUENCY_RESPONSE: + return Pel::tra('Spatial Frequency Response'); + case self::FOCAL_PLANE_X_RESOLUTION: + return Pel::tra('Focal Plane x-Resolution'); + case self::FOCAL_PLANE_Y_RESOLUTION: + return Pel::tra('Focal Plane y-Resolution'); + case self::FOCAL_PLANE_RESOLUTION_UNIT: + return Pel::tra('Focal Plane Resolution Unit'); + case self::SUBJECT_LOCATION: + return Pel::tra('Subject Location'); + case self::EXPOSURE_INDEX: + return Pel::tra('Exposure index'); + case self::SENSING_METHOD: + return Pel::tra('Sensing Method'); + case self::FILE_SOURCE: + return Pel::tra('File Source'); + case self::SCENE_TYPE: + return Pel::tra('Scene Type'); + case self::SUBJECT_AREA: + return Pel::tra('Subject Area'); + case self::CUSTOM_RENDERED: + return Pel::tra('Custom Rendered'); + case self::EXPOSURE_MODE: + return Pel::tra('Exposure Mode'); + case self::WHITE_BALANCE: + return Pel::tra('White Balance'); + case self::DIGITAL_ZOOM_RATIO: + return Pel::tra('Digital Zoom Ratio'); + case self::FOCAL_LENGTH_IN_35MM_FILM: + return Pel::tra('Focal Length In 35mm Film'); + case self::SCENE_CAPTURE_TYPE: + return Pel::tra('Scene Capture Type'); + case self::GAIN_CONTROL: + return Pel::tra('Gain Control'); + case self::CONTRAST: + return Pel::tra('Contrast'); + case self::SATURATION: + return Pel::tra('Saturation'); + case self::SHARPNESS: + return Pel::tra('Sharpness'); + case self::DEVICE_SETTING_DESCRIPTION: + return Pel::tra('Device Setting Description'); + case self::SUBJECT_DISTANCE_RANGE: + return Pel::tra('Subject Distance Range'); + case self::IMAGE_UNIQUE_ID: + return Pel::tra('Image Unique ID'); + case self::GAMMA: + return Pel::tra('Gamma'); + case self::PRINT_IM: + return Pel::tra('Print IM'); + } + + case PelIfd::GPS: + switch ($tag) { + case self::GPS_VERSION_ID: + return 'GPSVersionID'; + case self::GPS_LATITUDE_REF: + return 'GPSLatitudeRef'; + case self::GPS_LATITUDE: + return 'GPSLatitude'; + case self::GPS_LONGITUDE_REF: + return 'GPSLongitudeRef'; + case self::GPS_LONGITUDE: + return 'GPSLongitude'; + case self::GPS_ALTITUDE_REF: + return 'GPSAltitudeRef'; + case self::GPS_ALTITUDE: + return 'GPSAltitude'; + case self::GPS_TIME_STAMP: + return 'GPSTimeStamp'; + case self::GPS_SATELLITES: + return 'GPSSatellites'; + case self::GPS_STATUS: + return 'GPSStatus'; + case self::GPS_MEASURE_MODE: + return 'GPSMeasureMode'; + case self::GPS_DOP: + return 'GPSDOP'; + case self::GPS_SPEED_REF: + return 'GPSSpeedRef'; + case self::GPS_SPEED: + return 'GPSSpeed'; + case self::GPS_TRACK_REF: + return 'GPSTrackRef'; + case self::GPS_TRACK: + return 'GPSTrack'; + case self::GPS_IMG_DIRECTION_REF: + return 'GPSImgDirectionRef'; + case self::GPS_IMG_DIRECTION: + return 'GPSImgDirection'; + case self::GPS_MAP_DATUM: + return 'GPSMapDatum'; + case self::GPS_DEST_LATITUDE_REF: + return 'GPSDestLatitudeRef'; + case self::GPS_DEST_LATITUDE: + return 'GPSDestLatitude'; + case self::GPS_DEST_LONGITUDE_REF: + return 'GPSDestLongitudeRef'; + case self::GPS_DEST_LONGITUDE: + return 'GPSDestLongitude'; + case self::GPS_DEST_BEARING_REF: + return 'GPSDestBearingRef'; + case self::GPS_DEST_BEARING: + return 'GPSDestBearing'; + case self::GPS_DEST_DISTANCE_REF: + return 'GPSDestDistanceRef'; + case self::GPS_DEST_DISTANCE: + return 'GPSDestDistance'; + case self::GPS_PROCESSING_METHOD: + return 'GPSProcessingMethod'; + case self::GPS_AREA_INFORMATION: + return 'GPSAreaInformation'; + case self::GPS_DATE_STAMP: + return 'GPSDateStamp'; + case self::GPS_DIFFERENTIAL: + return 'GPSDifferential'; + } + + default: + return Pel::fmt('Unknown Tag: 0x%04X', $tag); + } + } + +} +?> \ No newline at end of file diff --git a/modules/autorotate/lib/pel/PelTiff.php b/modules/autorotate/lib/pel/PelTiff.php new file mode 100644 index 00000000..0146c9e2 --- /dev/null +++ b/modules/autorotate/lib/pel/PelTiff.php @@ -0,0 +1,296 @@ + + * @version $Revision: 458 $ + * @date $Date: 2006-11-18 22:22:58 +0100 (Sat, 18 Nov 2006) $ + * @license http://www.gnu.org/licenses/gpl.html GNU General Public + * License (GPL) + * @package PEL + */ + +/**#@+ Required class definitions. */ +require_once('PelDataWindow.php'); +require_once('PelIfd.php'); +require_once('Pel.php'); +/**#@-*/ + + +/** + * Class for handling TIFF data. + * + * Exif data is actually an extension of the TIFF file format. TIFF + * images consist of a number of {@link PelIfd Image File Directories} + * (IFDs), each containing a number of {@link PelEntry entries}. The + * IFDs are linked to each other --- one can get hold of the first one + * with the {@link getIfd()} method. + * + * To parse a TIFF image for Exif data one would do: + * + * + * $tiff = new PelTiff($data); + * $ifd0 = $tiff->getIfd(); + * $exif = $ifd0->getSubIfd(PelIfd::EXIF); + * $ifd1 = $ifd0->getNextIfd(); + * + * + * Should one have some image data of an unknown type, then the {@link + * PelTiff::isValid()} function is handy: it will quickly test if the + * data could be valid TIFF data. The {@link PelJpeg::isValid()} + * function does the same for JPEG images. + * + * @author Martin Geisler + * @package PEL + */ +class PelTiff { + + /** + * TIFF header. + * + * This must follow after the two bytes indicating the byte order. + */ + const TIFF_HEADER = 0x002A; + + /** + * The first Image File Directory, if any. + * + * If set, then the type of the IFD must be {@link PelIfd::IFD0}. + * + * @var PelIfd + */ + private $ifd = null; + + + /** + * Construct a new object for holding TIFF data. + * + * The new object will be empty (with no {@link PelIfd}) unless an + * argument is given from which it can initialize itself. This can + * either be the filename of a TIFF image or a {@link PelDataWindow} + * object. + * + * Use {@link setIfd()} to explicitly set the IFD. + */ + function __construct($data = false) { + if ($data === false) + return; + + if (is_string($data)) { + Pel::debug('Initializing PelTiff object from %s', $data); + $this->loadFile($data); + } elseif ($data instanceof PelDataWindow) { + Pel::debug('Initializing PelTiff object from PelDataWindow.'); + $this->load($data); + } else { + throw new PelInvalidArgumentException('Bad type for $data: %s', + gettype($data)); + } + } + + + /** + * Load TIFF data. + * + * The data given will be parsed and an internal tree representation + * will be built. If the data cannot be parsed correctly, a {@link + * PelInvalidDataException} is thrown, explaining the problem. + * + * @param PelDataWindow the data from which the object will be + * constructed. This should be valid TIFF data, coming either + * directly from a TIFF image or from the Exif data in a JPEG image. + */ + function load(PelDataWindow $d) { + Pel::debug('Parsing %d bytes of TIFF data...', $d->getSize()); + + /* There must be at least 8 bytes available: 2 bytes for the byte + * order, 2 bytes for the TIFF header, and 4 bytes for the offset + * to the first IFD. */ + if ($d->getSize() < 8) + throw new PelInvalidDataException('Expected at least 8 bytes of TIFF ' . + 'data, found just %d bytes.', + $d->getSize()); + + /* Byte order */ + if ($d->strcmp(0, 'II')) { + Pel::debug('Found Intel byte order'); + $d->setByteOrder(PelConvert::LITTLE_ENDIAN); + } elseif ($d->strcmp(0, 'MM')) { + Pel::debug('Found Motorola byte order'); + $d->setByteOrder(PelConvert::BIG_ENDIAN); + } else { + throw new PelInvalidDataException('Unknown byte order found in TIFF ' . + 'data: 0x%2X%2X', + $d->getByte(0), $d->getByte(1)); + } + + /* Verify the TIFF header */ + if ($d->getShort(2) != self::TIFF_HEADER) + throw new PelInvalidDataException('Missing TIFF magic value.'); + + /* IFD 0 offset */ + $offset = $d->getLong(4); + Pel::debug('First IFD at offset %d.', $offset); + + if ($offset > 0) { + /* Parse the first IFD, this will automatically parse the + * following IFDs and any sub IFDs. */ + $this->ifd = new PelIfd(PelIfd::IFD0); + $this->ifd->load($d, $offset); + } + } + + + /** + * Load data from a file into a TIFF object. + * + * @param string the filename. This must be a readable file. + */ + function loadFile($filename) { + $this->load(new PelDataWindow(file_get_contents($filename))); + } + + + /** + * Set the first IFD. + * + * @param PelIfd the new first IFD, which must be of type {@link + * PelIfd::IFD0}. + */ + function setIfd(PelIfd $ifd) { + if ($ifd->getType() != PelIfd::IFD0) + throw new PelInvalidDataException('Invalid type of IFD: %d, expected %d.', + $ifd->getType(), PelIfd::IFD0); + + $this->ifd = $ifd; + } + + + /** + * Return the first IFD. + * + * @return PelIfd the first IFD contained in the TIFF data, if any. + * If there is no IFD null will be returned. + */ + function getIfd() { + return $this->ifd; + } + + + /** + * Turn this object into bytes. + * + * TIFF images can have {@link PelConvert::LITTLE_ENDIAN + * little-endian} or {@link PelConvert::BIG_ENDIAN big-endian} byte + * order, and so this method takes an argument specifying that. + * + * @param PelByteOrder the desired byte order of the TIFF data. + * This should be one of {@link PelConvert::LITTLE_ENDIAN} or {@link + * PelConvert::BIG_ENDIAN}. + * + * @return string the bytes representing this object. + */ + function getBytes($order = PelConvert::LITTLE_ENDIAN) { + if ($order == PelConvert::LITTLE_ENDIAN) + $bytes = 'II'; + else + $bytes = 'MM'; + + /* TIFF magic number --- fixed value. */ + $bytes .= PelConvert::shortToBytes(self::TIFF_HEADER, $order); + + if ($this->ifd != null) { + /* IFD 0 offset. We will always start IDF 0 at an offset of 8 + * bytes (2 bytes for byte order, another 2 bytes for the TIFF + * header, and 4 bytes for the IFD 0 offset make 8 bytes + * together). + */ + $bytes .= PelConvert::longToBytes(8, $order); + + /* The argument specifies the offset of this IFD. The IFD will + * use this to calculate offsets from the entries to their data, + * all those offsets are absolute offsets counted from the + * beginning of the data. */ + $bytes .= $this->ifd->getBytes(8, $order); + } else { + $bytes .= PelConvert::longToBytes(0, $order); + } + + return $bytes; + } + + + /** + * Return a string representation of this object. + * + * @return string a string describing this object. This is mostly useful + * for debugging. + */ + function __toString() { + $str = Pel::fmt("Dumping TIFF data...\n"); + if ($this->ifd != null) + $str .= $this->ifd->__toString(); + + return $str; + } + + + /** + * Check if data is valid TIFF data. + * + * This will read just enough data from the data window to determine + * if the data could be a valid TIFF data. This means that the + * check is more like a heuristic than a rigorous check. + * + * @param PelDataWindow the bytes that will be examined. + * + * @return boolean true if the data looks like valid TIFF data, + * false otherwise. + * + * @see PelJpeg::isValid() + */ + static function isValid(PelDataWindow $d) { + /* First check that we have enough data. */ + if ($d->getSize() < 8) + return false; + + /* Byte order */ + if ($d->strcmp(0, 'II')) { + $d->setByteOrder(PelConvert::LITTLE_ENDIAN); + } elseif ($d->strcmp(0, 'MM')) { + Pel::debug('Found Motorola byte order'); + $d->setByteOrder(PelConvert::BIG_ENDIAN); + } else { + return false; + } + + /* Verify the TIFF header */ + return $d->getShort(2) == self::TIFF_HEADER; + } + +} \ No newline at end of file diff --git a/modules/autorotate/module.info b/modules/autorotate/module.info index 17c62229..42a108fb 100644 --- a/modules/autorotate/module.info +++ b/modules/autorotate/module.info @@ -1,3 +1,3 @@ name = "Autorotate" description = "Rotate an image automatically on upload based on EXIF data" -version = 1 \ No newline at end of file +version = 2 \ No newline at end of file diff --git a/modules/basket/controllers/admin_configure.php b/modules/basket/controllers/admin_configure.php index e028d512..7831734d 100644 --- a/modules/basket/controllers/admin_configure.php +++ b/modules/basket/controllers/admin_configure.php @@ -1,7 +1,7 @@ validate()) { basket::extractForm($form); message::success(t("Basket Module Configured!")); - //url::redirect("admin/recaptcha"); } } else @@ -45,11 +43,121 @@ class Admin_Configure_Controller extends Controller $view = new Admin_View("admin.html"); $view->content = new View("admin_configure.html"); - - $view->content->form = $form; - //$view->content->products = ORM::factory("product")->order_by("name")->find_all(); print $view; } + + /** + * the index page of the user homes admin + */ + public function templates() + { + $form = basket::get_template_form(); + if (request::method() == "post") { + access::verify_csrf(); + + if ($form->validate()) { + + basket::extractTemplateForm($form); + message::success(t("Basket Module Configured!")); + } + } + else + { + basket::populateTemplateForm($form); + } + + $view = new Admin_View("admin.html"); + $view->content = new View("admin_templates.html"); + + $view->content->form = $form; + + print $view; + } + + public function paypal_encrypt_wizard_step1() + { + $view = new Admin_View("admin.html"); + $view->content = new View("pew1.html"); + + $view->content->form = self::keyGenerationForm(); + + print $view; + + } + + public function paypal_encrypt_wizard_step2() + { + access::verify_csrf(); + + $form = self::keyGenerationForm(); + + if (!$form->validate()) { + + self::paypal_encrypt_wizard_step1(); + return; + } + + $ssldir = str_replace('\\','/',VARPATH.'certificate'); + $ssldir= rtrim($ssldir, '/').'/'; + + if ( ! is_dir($ssldir)) + { + // Create the upload directory + mkdir($ssldir, 0777, TRUE); + } + + $prkeyfile = $ssldir . "myprvkey.pem"; + $pubcertfile = $ssldir . "mypubcert.pem"; + $certreqfile = $ssldir . "mycertreq.pem"; + + $dn = array("countryName" => $form->encrypt->countryName->value, + "stateOrProvinceName" => $form->encrypt->stateOrProvinceName->value, + "localityName" => $form->encrypt->localityName->value, + "organizationName" => $form->encrypt->organizationName->value, + "organizationalUnitName" => $form->encrypt->organizationalUnitName->value, + "commonName" => $form->encrypt->commonName->value, + "emailAddress" => $form->encrypt->emailAddress->value); + $privkeypass = $form->encrypt->privKeyPass->value; + $numberofdays = 365; + $config = array( + "private_key_bits" => 1024 + ); + + $privkey = openssl_pkey_new($config); + $csr = openssl_csr_new($dn, $privkey); + $sscert = openssl_csr_sign($csr, null, $privkey, $numberofdays); + openssl_x509_export($sscert, $publickey); + openssl_pkey_export($privkey, $privatekey, $privkeypass); + openssl_csr_export($csr, $csrStr); + + openssl_x509_export_to_file($sscert, $pubcertfile); + openssl_pkey_export_to_file ($privkey, $prkeyfile, $privkeypass); + openssl_csr_export_to_file($csr, $certreqfile); + + //echo "Your Public Certificate has been saved to " . $pubcertfile . "

"; + //echo "Your Private Key has been saved to " . $prkeyfile . "

"; + //echo "Your Certificate Request has been saved to " . $certreqfile . "

"; + + //echo $privatekey; // Will hold the exported PriKey + //echo $publickey; // Will hold the exported PubKey + //echo $csrStr; // Will hold the exported Certificate + } + + private function keyGenerationForm() + { + $form = new Forge("admin/configure/paypal_encrypt_wizard_step2", "", "post", array("id" => "generateKeys", "name" =>"generateKeys")); + $group = $form->group("encrypt")->label(t("Key Generation Details")); + $group->input("countryName")->label(t("Country Name"))->id("countryName"); + $group->input("stateOrProvinceName")->label(t("State or Province Name"))->id("stateOrProvinceName"); + $group->input("localityName")->label(t("Locality Name"))->id("localityName"); + $group->input("organizationName")->label(t("Organization Name"))->id("organizationName"); + $group->input("organizationalUnitName")->label(t("Organizational Unit Name"))->id("organizationalUnitName"); + $group->input("commonName")->label(t("Common Name"))->id("commonName"); + $group->input("emailAddress")->label(t("E-Mail Address"))->id("emailAddress"); + $group->input("privKeyPass")->label(t("Private Key Pass"))->id("privkeypass"); + return $form; + } + } diff --git a/modules/basket/controllers/admin_postage_bands.php b/modules/basket/controllers/admin_postage_bands.php index d1b9dc97..914d7537 100644 --- a/modules/basket/controllers/admin_postage_bands.php +++ b/modules/basket/controllers/admin_postage_bands.php @@ -1,7 +1,7 @@ validate(); - $postage_band = ORM::factory("postage_band"); - $postage_band->name = $form->add_postage->inputs["name"]->value; - $postage_band->flat_rate = $form->add_postage->flat_rate->value; - $postage_band->per_item = $form->add_postage->per_item->value; - $postage_band->validate(); - } catch (ORM_Validation_Exception $e) { - // Translate ORM validation errors into form error messages - foreach ($e->validation->errors() as $key => $error) { - $form->add_postage->inputs[$key]->add_error($error, 1); - } + $valid = $form->validate(); + $name = $form->add_postage->inputs["name"]->value; + $postage = ORM::factory("postage_band")->where("name","=", $name)->find(); + if ($postage->loaded()) { + $form->add_postage->inputs["name"]->add_error("in_use", 1); $valid = false; } if ($valid) { - $postage_band->save(); + $postage = postage_band::create( + $name, + $form->add_postage->flat_rate->value, + $form->add_postage->per_item->value + ); + + $postage->save(); message::success(t("Created postage band %postage_name", array( - "postage_name" => html::clean($postage_band->name)))); - json::reply(array("result" => "success")); + "postage_name" => html::clean($postage->name)))); + print json::reply(array("result" => "success")); } else { - json::reply(array("result" => "error", "html" => (string)$form)); + print $form; } } public function delete_postage_band_form($id) { $postage = ORM::factory("postage_band", $id); if (!$postage->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } print postage_band::get_delete_form_admin($postage); } @@ -77,13 +76,9 @@ class Admin_Postage_Bands_Controller extends Controller public function delete_postage_band($id) { access::verify_csrf(); - if ($id == user::active()->id || $id == user::guest()->id) { - access::forbidden(); - } - $postage = ORM::factory("postage_band", $id); if (!$postage->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } $form = postage_band::get_delete_form_admin($postage); @@ -91,13 +86,13 @@ class Admin_Postage_Bands_Controller extends Controller $name = $postage->name; $postage->delete(); } else { - json::reply(array("result" => "error", "html" => (string)$form)); + print $form; } $message = t("Deleted user %postage_band", array("postage_band" => html::clean($name))); log::success("user", $message); message::success($message); - json::reply(array("result" => "success")); + print json::reply(array("result" => "success")); } public function edit_postage_band($id) { @@ -105,38 +100,43 @@ class Admin_Postage_Bands_Controller extends Controller $postage = ORM::factory("postage_band", $id); if (!$postage->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } $form = postage_band::get_edit_form_admin($postage); - try { - $valid = $form->validate(); - $postage->name = $form->edit_postage->inputs["name"]->value; - $postage->flat_rate = $form->edit_postage->flat_rate->value; - $postage->per_item = $form->edit_postage->per_item->value; - $postage->validate(); - } catch (ORM_Validation_Exception $e) { - // Translate ORM validation errors into form error messages - foreach ($e->validation->errors() as $key => $error) { - $form->edit_postage->inputs[$key]->add_error($error, 1); + $valid = $form->validate(); + if ($valid) { + $new_name = $form->edit_postage->inputs["name"]->value; + if ($new_name != $postage->name && + ORM::factory("postage_band") + ->where("name", "=", $new_name) + ->where("id","!=", $postage->id) + ->find() + ->loaded()) { + $form->edit_postage->inputs["name"]->add_error("in_use", 1); + $valid = false; + } else { + $postage->name = $new_name; } - $valid = false; } if ($valid) { + $postage->flat_rate = $form->edit_postage->flat_rate->value; + $postage->per_item = $form->edit_postage->per_item->value; $postage->save(); + message::success(t("Changed postage band %postage_name", array("postage_name" => html::clean($postage->name)))); - json::reply(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - json::reply(array("result" => "error", "html" => (string)$form)); + print $form; } } public function edit_postage_band_form($id) { $postage = ORM::factory("postage_band", $id); if (!$postage->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } $form = postage_band::get_edit_form_admin($postage); diff --git a/modules/basket/controllers/admin_product_lines.php b/modules/basket/controllers/admin_product_lines.php index bc01c19b..752f4e63 100644 --- a/modules/basket/controllers/admin_product_lines.php +++ b/modules/basket/controllers/admin_product_lines.php @@ -1,7 +1,7 @@ validate(); - $product = ORM::factory("product"); - $product->name = $form->add_product->inputs["name"]->value; - $product->description = $form->add_product->description->value; - $product->postage_band_id = $form->add_product->postage_band->value; - $product->validate(); - } catch (ORM_Validation_Exception $e) { - // Translate ORM validation errors into form error messages - foreach ($e->validation->errors() as $key => $error) { - $form->add_product->inputs[$key]->add_error($error, 1); - } + $valid = $form->validate(); + $name = $form->add_product->inputs["name"]->value; + $product = ORM::factory("product")->where("name", "=", $name)->find(); + if ($product->loaded()) { + $form->add_product->inputs["name"]->add_error("in_use", 1); $valid = false; } if ($valid) { + $product = product::create( + $name, + $form->add_product->cost->value, + $form->add_product->description->value, + $form->add_product->postage_band->value + ); + $product->save(); message::success(t("Created product %product_name", array( "product_name" => html::clean($product->name)))); - json::reply(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - json::reply(array("result" => "error", "html" => (string)$form)); + print $form; } } public function delete_product_form($id) { $product = ORM::factory("product", $id); if (!$product->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } print product::get_delete_form_admin($product); } @@ -77,13 +77,9 @@ class Admin_Product_Lines_Controller extends Controller public function delete_product($id) { access::verify_csrf(); - if ($id == user::active()->id || $id == user::guest()->id) { - access::forbidden(); - } - $product = ORM::factory("product", $id); if (!$product->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } $form = product::get_delete_form_admin($product); @@ -91,13 +87,13 @@ class Admin_Product_Lines_Controller extends Controller $name = $product->name; $product->delete(); } else { - json::reply(array("result" => "error", "html" => (string)$form)); + print $form; } $message = t("Deleted user %product_name", array("product_name" => html::clean($name))); log::success("user", $message); message::success($message); - json::reply(array("result" => "success")); + print json::reply(array("result" => "success")); } public function edit_product($id) { @@ -105,39 +101,44 @@ class Admin_Product_Lines_Controller extends Controller $product = ORM::factory("product", $id); if (!$product->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } $form = product::get_edit_form_admin($product); - try { - $valid = $form->validate(); - $product->name = $form->edit_product->inputs["name"]->value; - $product->cost = $form->edit_product->cost->value; - $product->description = $form->edit_product->description->value; - $product->postage_band_id = $form->edit_product->postage_band->value; - $product->validate(); - } catch (ORM_Validation_Exception $e) { - // Translate ORM validation errors into form error messages - foreach ($e->validation->errors() as $key => $error) { - $form->edit_product->inputs[$key]->add_error($error, 1); + $valid = $form->validate(); + if ($valid) { + $new_name = $form->edit_product->inputs["name"]->value; + if ($new_name != $product->name && + ORM::factory("product") + ->where("name", "=", $new_name) + ->where("id","!=", $product->id) + ->find() + ->loaded()) { + $form->edit_product->inputs["name"]->add_error("in_use", 1); + $valid = false; + } else { + $product->name = $new_name; } - $valid = false; } if ($valid) { + $product->cost = $form->edit_product->cost->value; + $product->description = $form->edit_product->description->value; + $product->postage_band_id = $form->edit_product->postage_band->value; $product->save(); + message::success(t("Changed product %product_name", array("product_name" => html::clean($product->name)))); - json::reply(array("result" => "success")); + print json::reply(array("result" => "success")); } else { - json::reply(array("result" => "error", "html" => (string)$form)); + print $form; } } public function edit_product_form($id) { $product = ORM::factory("product", $id); if (!$product->loaded()) { - throw new Kohana_404_Exception(); + kohana::show_404(); } $form = product::get_edit_form_admin($product); diff --git a/modules/basket/controllers/basket.php b/modules/basket/controllers/basket.php index 612708c4..a4ecbee3 100644 --- a/modules/basket/controllers/basket.php +++ b/modules/basket/controllers/basket.php @@ -1,7 +1,7 @@ query("ALTER TABLE {orders} ADD COLUMN `method` int(9) DEFAULT 0;"); + } + public function view_basket($pp="") { - $template = new Theme_View("page.html", "other", "basket"); + $template = new Theme_View("page.html", "basket"); + + $basket = Session_Basket::get(); + if (isset($pp)){ + if ($pp=="nopp"){ + $basket->disablepp(); + } + elseif ($pp=="ppon"){ + $basket->enablepp(); + } + } $view = new View("view_basket.html"); - $view->basket = Session_Basket::get(); + $view->basket = $basket; + $template->content = $view; print $template; } - private function getCheckoutForm(){ + public function preview($id) { + $item = ORM::factory("item", $id); + + print ""; + + } + + public function view_orders() { + self::check_view_orders(); + $template = new Theme_View("page.html", "basket"); + + $incomplete_orders = ORM::factory("order")->where('status',"<",20)->find_all(); + + $view = new View("view_orders.html"); + + $view->orders = $incomplete_orders; + + $template->content = $view; + + print $template; + } + + + public function view_ipn($orderid){ + self::check_view_orders(); + + $template = new Theme_View("page.html", "basket"); + + $order = ORM::factory("order")->where("id","=",$orderid)->find(); + $ipn_messages = ORM::factory("ipn_message")->where("key","=",$orderid)->find_all(); + //$ipn_messages = ORM::factory("ipn_message")->find_all(); + + $view = new View("view_ipn.html"); + + $view->order = $order; + $view->ipn_messages = $ipn_messages; + + $template->content = $view; + + print $template; + + } + + public function check_view_orders() { + if (!basket::can_view_orders()){ + die("Invalid access."); + } + } + + public function print_order($id){ + + access::verify_csrf(); + self::check_view_orders(); + + + $prefix = basket::getOrderPrefix(); + $length = strlen($prefix); + if (strlen($id)>$length ){ + if ($prefix === strtolower(substr($id,0,$length ))){ + $id = substr($id,$length); + } + } + $order = ORM::factory("order", $id); + $view = new View("print_order.html"); + + if ($order->loaded()){ + $view->order = str_replace(array("\r\n", "\n", "\r"),"
",$order->text); + }else{ + $view->order = "Order ".$id." not found."; + } + print $view; + } + + public function show_order($id){ + + access::verify_csrf(); + self::check_view_orders(); + $prefix = basket::getOrderPrefix(); + $length = strlen($prefix); + if (strlen($id)>$length ){ + if ($prefix === strtolower(substr($id,0,$length ))){ + $id = substr($id,$length); + } + } + + $order = ORM::factory("order", $id); + + if ($order->loaded()){ + $view = new View("view_order.html"); + $view->order = $order; + print $view; + }else{ + print "Order ".$id." not found."; + } + } + + public function show_ipn($id){ + access::verify_csrf(); + self::check_view_orders(); + $ipn_message = ORM::factory("ipn_message", $id); + + if ($ipn_message->loaded()){ + print $ipn_message->text; + }else{ + print "IPN Message ".$id." not found."; + } + + } + + public function confirm_order_delivery($id){ + access::verify_csrf(); + self::check_view_orders(); + $order = ORM::factory("order", $id); + + if ($order->loaded()){ + if ($order->status == 2) + { + $order->status = 20; + $order->save(); + } + } + url::redirect("basket/view_orders"); + } + + public function confirm_order_payment($id){ + access::verify_csrf(); + self::check_view_orders(); + $order = ORM::factory("order", $id); + + if ($order->loaded()){ + if ($order->status == 1) + { + $order->status = 2; + $order->save(); + } + } + url::redirect("basket/view_orders"); + } + + private function getCheckoutForm(){ $form = new Forge("basket/confirm", "", "post", array("id" => "checkout", "name" =>"checkout")); $group = $form->group("contact")->label(t("Contact Details")); $group->input("fullname")->label(t("Name"))->id("fullname"); @@ -42,13 +196,14 @@ class Basket_Controller extends Controller { $group->input("postcode")->label(t("Postcode"))->id("postcode"); $group->input("email")->label(t("E-Mail Address"))->id("email"); $group->input("phone")->label(t("Telephone Number"))->id("phone"); + $group->hidden("paypal")->id("paypal"); return $form; } public function checkout () { - $template = new Theme_View("page.html", "other", "basket"); + $template = new Theme_View("page.html", "basket"); $view = new View("checkout.html"); @@ -80,6 +235,12 @@ class Basket_Controller extends Controller { if ($valid){ $basket = Session_Basket::get(); + + if (!isset($basket->contents ) || count($basket->contents) == 0) { + self::view_basket(); + return; + } + $basket->name = $form->contact->fullname->value; $basket->house = $form->contact->house->value; $basket->street = $form->contact->street->value; @@ -89,14 +250,33 @@ class Basket_Controller extends Controller { $basket->email = $form->contact->email->value; $basket->phone = $form->contact->phone->value; - $template = new Theme_View("page.html", "other", "basket"); + $paypal=$form->contact->paypal->value=="true"; + $template = new Theme_View("page.html", "basket"); - $form = new Forge("basket/complete", "", "post", array("id" => "confirm", "name" =>"confirm")); - $view = new View("confirm_order.html"); - $view->basket = $basket; - $template->content = $view; - $view->form = $form; - print $template; + if ($paypal){ + // create a prelimary order + $order = basket::createOrder($basket, Order_Model::PAYMENT_PAYPAL); + $paypal = new Paypal(); + + // create the order first + $view = new View("paypal_redirect.html"); + $view ->form = $paypal->process($basket, + url::site("basket/paypal_complete/$order->id", "http"), + url::site("basket/paypal_cancel/$order->id", "http"), + url::site("basket/paypal_ipn/$order->id", "http")); + $template->content = $view; + print $template; + + // redirect to paypal + }else + { + $form = new Forge("basket/complete", "", "post", array("id" => "confirm", "name" =>"confirm")); + $view = new View("confirm_order.html"); + $view->basket = $basket; + $template->content = $view; + $view->form = $form; + print $template; + } } else { @@ -105,65 +285,92 @@ class Basket_Controller extends Controller { } } - public function complete () { - access::verify_csrf(); + function paypal_ipn($id){ + $order = ORM::factory("order")->where("id","=",$id)->find(); + if ($order->loaded()){ + + $paypal = new Paypal(); + + if ($paypal->validate_ipn($id)){ + if ($paypal->ipn_data['payment_status'] == "Completed"){ + + $order->status = Order_Model::PAYMENT_CONFIRMED; + + // send e-mails + basket::send_order($order); + basket::send_invoice($order); + + $order->save(); + } + return; + } + print "invalid access. tut tut!"; + } + return; + + } + + public function paypal_complete($id) { + $order = ORM::factory("order")->where("id","=",$id)->find(); $basket = Session_Basket::get(); + $basket->clear(); + $this->_complete($order); + } - //$admin_address = basket::getEmailAddress(); - $postage = $basket->postage_cost(); - $product_cost = $basket->cost(); + public function paypal_cancel($id){ + $order = ORM::factory("order")->where("id","=",$id)->find(); - $admin_email = "Order for : -".$basket->name." -".$basket->house." -".$basket->street." -".$basket->suburb." -".$basket->town." -".$basket->postcode." -".$basket->email." -".$basket->phone." -Placed at ".date("d F Y - H:i" ,time())." -Cost of Ordered Products = ".$product_cost." -Postage and Packaging Costs + ".$postage." -Total Owed ".($product_cost+$postage)." Total in ".basket::getCurrency()." - -Items Ordered: - -"; - - // create the order items - foreach ($basket->contents as $basket_item){ - $item = $basket_item->getItem(); - $prod = ORM::factory("product", $basket_item->product); - $admin_email = $admin_email." -".$item->title." - ".$item->url()." -".$prod->name." - ".$prod->description." -".$basket_item->quantity." @ ".$prod->cost." - -"; + if ($order->loaded()){ + $order->delete(); } + $this->checkout(); + } - $from = "From: ".basket::getEmailAddress(); - mail(basket::getEmailAddress(), "Order from ".$basket->name, $admin_email, $from); + public function complete () { + access::verify_csrf(); + $basket = Session_Basket::get(); + + if (!isset($basket->contents ) || count($basket->contents) == 0) { + self::view_basket(); + return; + } + + // create order + $order = basket::createOrder($basket, Order_Model::PAYMENT_OFFLINE); $basket->clear(); - $template = new Theme_View("page.html", "other", "basket"); + // send e-mails + basket::send_order($order); + basket::send_invoice($order); + + + $this->_complete($order); + } + + private function _complete($order){ + $template = new Theme_View("page.html", "basket"); $view = new View("order_complete.html"); + $ordernumber = basket::getOrderPrefix().$order->id; + $view->ordernumber = $ordernumber; + $view->order = $order; + $view->total_cost = $order->cost; + $template->content = $view; + print $template; } private function getAddToBasketForm($id){ - $form = new Forge("basket/add_to_basket", "", "post", array("id" => "g-add-to-basket-form")); + $form = new Forge("basket/add_to_basket", "", "post", array("id" => "gAddToBasketForm")); $group = $form->group("add_to_basket")->label(t("Add To Basket")); $group->hidden("id"); $group->dropdown("product") ->label(t("Product")) ->options(product::getProductArray($id)); - $group->input("quantity")->label(t("Quantity"))->id("g-quantity"); + $group->input("quantity")->label(t("Quantity"))->id("gQuantity"); $group->submit("")->value(t("Add")); //$group->submit("proceedToCheckout")->value(t("Proceed To Checkout")); @@ -189,7 +396,11 @@ Items Ordered: $form->add_to_basket->product->value, $form->add_to_basket->quantity->value); - json::reply(array("result" => "success")); + $item = ORM::factory("item", $form->add_to_basket->id->value); + + Session::instance()->set("redirect_home", $item->parent_id); + + print json::reply(array("result" => "success")); } else { diff --git a/modules/basket/css/basket.css b/modules/basket/css/basket.css index f660685d..107b8229 100644 --- a/modules/basket/css/basket.css +++ b/modules/basket/css/basket.css @@ -1,5 +1,17 @@ #basket {float:right;} #add_to_basket {float:right} -#basketForm {max-width:200px} -#basketThumb {float:left; padding:10px 10px 0 0;} +#basketForm {max-width:200px;float:left;} +#basketThumb {float:left; padding:10px;} #basketThumb img{max-width:100px;} +#payment {float:right; width:50%} +#checkout input, +#checkout select, +#checkout textarea { + display: block; + clear: both; + padding: .2em; + width: 100%; +} +#sidebar-basket {max-height:300px; overflow-y:auto; overflow-x:hidden;} +.order-status-1 a{color:#AA0000 !important} +.order-status-2 a{color:#00AA00 !important} diff --git a/modules/basket/helpers/basket.php b/modules/basket/helpers/basket.php index 7f1c4661..b9be6726 100644 --- a/modules/basket/helpers/basket.php +++ b/modules/basket/helpers/basket.php @@ -1,7 +1,7 @@ "Mexican Peso"); static $format= array( + "AUD" => "$", + "CAD" => "$", + "EUR" => "€", + "GBP" => "£", + "JPY" => "¥", + "USD" => "$", + "NZD" => "$", + "CHF" => "", + "HKD" => "$", + "SGD" => "$", + "SEK" => "", + "DKK" => "", + "PLN" => "", + "NOK" => "", + "HUF" => "", + "CZK" => "", + "ILS" => "", + "MXN" => ""); + + static $formatweb= array( "AUD" => "$", "CAD" => "$", "EUR" => "€", @@ -60,37 +80,108 @@ class basket_Core { "MXN" => ""); - static function get_configure_form() { + static public function can_view_orders() + { + if (identity::active_user()->admin){ + return true; + } + + print identity::active_user(); + foreach (identity::active_user()->groups() as $group){ + if ($group->name == 'shop'){ + return true; + } + } + + return false; + } + + + static function get_configure_form() { $form = new Forge("admin/configure", "", "post", array("id" => "g-configure-form")); $group = $form->group("configure")->label(t("Configure Basket")); $group->input("email")->label(t("Offline Paying Email Address"))->id("g-order-email-address"); $group->dropdown("currency") - ->label(t("Currency")) - ->options(self::$currencies); + ->label(t("Currency")) + ->options(self::$currencies); + + $group->checkbox("side_bar")->label(t("Use only side bar"))->id("g-side-bar-only"); $group->checkbox("paypal")->label(t("Use Paypal"))->id("g-paypal"); $group->input("paypal_account")->label(t("Paypal E-Mail Address"))->id("g-paypal-address"); + $group->checkbox("allow_pickup")->label(t("Allow Product Pickup"))->id("g-allow-pickup"); + $group->input("order_prefix")->label(t("Order Number Prefix"))->id("g-order-prefix"); + $group->submit("")->value(t("Save")); + return $form; + } + + static function get_template_form() { + $form = new Forge("admin/configure/templates", "", "post", array("id" => "g-configure-form")); + $group = $form->group("configure")->label(t("Configure Basket")); + $group->textarea("payment_details")->label(t("Payment Details Description"))->id("g-payment-details"); + $group->textarea("order_complete_page")->label(t("Order Complete Page"))->id("g-order-complete_page"); + $group->input("order_complete_email_subject")->label(t("Order Complete Email Subject"))->id("g-order-complete_email_subject"); + $group->textarea("order_complete_email")->label(t("Order Complete Email"))->id("g-order-complete_email"); $group->submit("")->value(t("Save")); return $form; } static function populateForm($form){ - $form->configure->email->value(basket::getEmailAddress()); - $form->configure->paypal->checked(basket::isPaypal()); - $form->configure->paypal_account->value(basket::getPaypalAccount()); - $form->configure->currency->selected(basket::getCurrency()); + $form->configure->email->value(basket::getEmailAddress()); + $form->configure->side_bar->checked(basket::is_side_bar_only()); + $form->configure->paypal->checked(basket::isPaypal()); + $form->configure->paypal_account->value(basket::getPaypalAccount()); + $form->configure->currency->selected(basket::getCurrency()); + $form->configure->allow_pickup->checked(basket::isAllowPickup()); + $form->configure->order_prefix->value(basket::getOrderPrefix()); + } + + static function populateTemplateForm($form){ + $form->configure->payment_details->value(basket::getPaymentDetails()); + $form->configure->order_complete_page->value(basket::getOrderCompletePage()); + $form->configure->order_complete_email_subject->value(basket::getOrderCompleteEmailSubject()); + $form->configure->order_complete_email->value(basket::getOrderCompleteEmail()); } static function extractForm($form){ - $email = $form->configure->email->value; - $isPaypal = $form->configure->paypal->value; - $paypal_account = $form->configure->paypal_account->value; - $currency = $form->configure->currency->selected; - basket::setEmailAddress($email); - basket::setPaypal($isPaypal); - basket::setPaypalAccount($paypal_account); - basket::setCurrency($currency); + $email = $form->configure->email->value; + $is_side_bar = $form->configure->side_bar->value; + $isPaypal = $form->configure->paypal->value; + $paypal_account = $form->configure->paypal_account->value; + $currency = $form->configure->currency->selected; + $allow_pickup = $form->configure->allow_pickup->value; + $order_prefix = $form->configure->order_prefix->value; + basket::setEmailAddress($email); + basket::set_side_bar_only($is_side_bar); + basket::setPaypal($isPaypal); + basket::setPaypalAccount($paypal_account); + basket::setCurrency($currency); + basket::setAllowPickup($allow_pickup); + basket::setOrderPrefix($order_prefix); } + static function extractTemplateForm($form){ + $payment_details = $form->configure->payment_details->value; + $order_complete_page = $form->configure->order_complete_page->value; + $order_complete_email_subject = $form->configure->order_complete_email_subject->value; + $order_complete_email = $form->configure->order_complete_email->value; + basket::setPaymentDetails($payment_details); + basket::setOrderCompletePage($order_complete_page); + basket::setOrderCompleteEmailSubject($order_complete_email_subject); + basket::setOrderCompleteEmail($order_complete_email); + } + + static public function is_side_bar_only() + { + return module::get_var("basket","is_side_bar_only"); + + } + + static public function set_side_bar_only($value) + { + module::set_var("basket","is_side_bar_only",$value); + + } + static function getEmailAddress(){ return module::get_var("basket","email"); @@ -113,10 +204,51 @@ class basket_Core { return $cur; } + static function getPaymentDetails(){ + return module::get_var("basket","payment_details"); + } + + static function getOrderPrefix(){ + return module::get_var("basket","order_prefix"); + } + + static function isAllowPickup(){ + return module::get_var("basket","allow_pickup"); + } + + static function getOrderCompletePage(){ + return module::get_var("basket","order_complete_page"); + } + + static function getOrderCompleteEmail(){ + return module::get_var("basket","order_complete_email"); + } + + static function getOrderCompleteEmailSubject(){ + return module::get_var("basket","order_complete_email_subject"); + } + static function formatMoney($money){ return self::$format[self::getCurrency()].number_format($money,2); } + static function formatMoneyForWeb($money){ + return self::$formatweb[self::getCurrency()].number_format($money,2); + } + + static function replaceStrings($string, $key_values) { + // Replace x_y before replacing x. + krsort($key_values, SORT_STRING); + + $keys = array(); + $values = array(); + foreach ($key_values as $key => $value) { + $keys[] = "%$key"; + $values[] = $value; + } + return str_replace($keys, $values, $string); + } + static function setEmailAddress($email){ module::set_var("basket","email",$email); } @@ -133,32 +265,129 @@ class basket_Core { module::set_var("basket","currency",$currency); } - static function generatePaypalForm($session_basket){ - $form = " -
- - - -"; + static function setPaymentDetails($details){ + module::set_var("basket","payment_details",$details); + } - $postage = $session_basket->postage_cost(); - if ($postage > 0) { - $form = $form." -"; + static function setAllowPickup($allow_pickup){ + module::set_var("basket","allow_pickup",$allow_pickup); + } + + static function setOrderPrefix($order_prefix){ + module::set_var("basket","order_prefix",strtolower($order_prefix)); + } + + static function setOrderCompletePage($details){ + module::set_var("basket","order_complete_page",$details); + } + + static function setOrderCompleteEmail($details){ + module::set_var("basket","order_complete_email",$details); + } + + static function setOrderCompleteEmailSubject($details){ + module::set_var("basket","order_complete_email_subject",$details); + } + + static function createOrder($basket, $method){ + + $order = ORM::factory("order"); + $order->text = "processing"; + $order->save(); + + $ordernumber = basket::getOrderPrefix().$order->id; + + //$admin_address = basket::getEmailAddress(); + $postage = $basket->postage_cost(); + $product_cost = $basket->cost(); + $ppon = $basket->ispp(); + + $text = " + Order Number : ".$ordernumber." + + for : +".$basket->name." +".$basket->house." +".$basket->street." +".$basket->suburb." +".$basket->town." +".$basket->postcode." +".$basket->email." +".$basket->phone." +Placed at ".date("d F Y - H:i" ,time())." +Cost of Ordered Products = ".$product_cost; + if ($ppon){ + $text = $text." +Postage and Packaging Costs + ".$postage." +Total Owed ".($product_cost+$postage)." Total in ".basket::getCurrency(); + } + else{ + $text = $text." +Person has chosen to pick up product. +Total Owed ".($product_cost)." Total in ".basket::getCurrency(); + } + $text = $text." + +Items Ordered: + +"; + + // create the order items + foreach ($basket->contents as $basket_item){ + $item = $basket_item->getItem(); + $prod = ORM::factory("product", $basket_item->product); + $text = $text." +".$item->title." - ".$item->url()." +".$prod->name." - ".$prod->description." +".$basket_item->quantity." @ ".$prod->cost." + +"; } - $id = 1; - foreach ($session_basket->contents as $key => $basket_item){ - $form = $form." -getCode()."\"/> -cost_per\"/> -quantity\"/>"; - $id++; + if ($ppon){ + $total_cost = ($product_cost+$postage); + } + else{ + $total_cost = $product_cost; } - $form = $form."
"; + $order->name = $basket->name; + $order->email = $basket->email; + $order->cost = $total_cost; + $order->text = $text; + $order->status = Order_Model::WAITING_PAYMENT; + $order->method = $method; + $order->save(); + + //$basket->clear(); + + return $order; + } + + public function send_order($order){ + + $from = "From: ".basket::getEmailAddress(); + $ordernumber = basket::getOrderPrefix().$order->id; + + mail(basket::getEmailAddress(), "Order ".$ordernumber." from ".$order->name, $order->text, $from); + + } + + public function send_invoice($order) + { + + $from = "From: ".basket::getEmailAddress(); + $ordernumber = basket::getOrderPrefix().$order->id; + $invoice_email = basket::replaceStrings(basket::getOrderCompleteEmail(),Array( + "name"=>$order->name, + "order_number"=> $ordernumber, + "total_cost" =>basket::formatMoney($order->cost), + "order_details"=>$order->text)); + + mail($order->email, + basket::replaceStrings(basket::getOrderCompleteEmailSubject(),Array("order_number"=>$ordernumber)), + $invoice_email, $from); - return $form; } } \ No newline at end of file diff --git a/modules/basket/helpers/basket_block.php b/modules/basket/helpers/basket_block.php new file mode 100644 index 00000000..9263a68e --- /dev/null +++ b/modules/basket/helpers/basket_block.php @@ -0,0 +1,21 @@ + t("Basket")); + } + + static function get($block_id, $theme) { + $block = ""; + switch ($block_id) { + case "shopping": + $block = new Block(); + $block->css_id = "g-view-basket"; + $block->title = t("Basket"); + $block->content = new View("basket-side-bar.html"); + $block->content->basket = Session_Basket::get(); + break; + } + return $block; + } +} \ No newline at end of file diff --git a/modules/basket/helpers/basket_event.php b/modules/basket/helpers/basket_event.php index 8e0b7df5..c22a48c6 100644 --- a/modules/basket/helpers/basket_event.php +++ b/modules/basket/helpers/basket_event.php @@ -1,7 +1,7 @@ label(t("Configure")) ->url(url::site("admin/configure"))); $basket_menu->append( + Menu::factory("link") + ->id("templates") + ->label(t("Templates")) + ->url(url::site("admin/configure/templates"))); + $basket_menu->append( Menu::factory("link") ->id("product_line") ->label(t("Product Lines")) @@ -43,6 +48,11 @@ class basket_event_Core{ ->id("postage_bands") ->label(t("Postage Bands")) ->url(url::site("admin/postage_bands"))); + $basket_menu->append( + Menu::factory("link") + ->id("view_orders") + ->label(t("View Orders")) + ->url(url::site("basket/view_orders"))); } @@ -65,8 +75,8 @@ class basket_event_Core{ if ($product_override->loaded()){ $item_product = ORM::factory("item_product") - ->where('product_override_id', "=", $product_override->id) - ->where('product_id', "=", $product->id)->find(); + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); if ($item_product->loaded()){ $checked = $item_product->include; if ($item_product->cost != -1){ diff --git a/modules/basket/helpers/basket_installer.php b/modules/basket/helpers/basket_installer.php index 78bad98f..b92cd694 100644 --- a/modules/basket/helpers/basket_installer.php +++ b/modules/basket/helpers/basket_installer.php @@ -1,7 +1,7 @@ query("CREATE TABLE IF NOT EXISTS {products} ( `id` int(9) NOT NULL auto_increment, `name` TEXT NOT NULL, @@ -30,14 +31,14 @@ class basket_installer `description` varchar(1024), `postage_band_id` int(9) default 1, PRIMARY KEY (`id`)) - DEFAULT CHARSET=utf8;"); + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE IF NOT EXISTS {product_overrides} ( `id` int(9) NOT NULL auto_increment, `item_id` int(9) NOT NULL, `none` BOOLEAN default false, PRIMARY KEY (`id`)) - DEFAULT CHARSET=utf8;"); + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); $db->query("CREATE TABLE IF NOT EXISTS {item_products} ( `id` int(9) NOT NULL auto_increment, @@ -54,34 +55,72 @@ class basket_installer `flat_rate` DECIMAL(10,2) default 0, `per_item` DECIMAL(10,2) default 0, PRIMARY KEY (`id`)) - DEFAULT CHARSET=utf8;"); + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - $postage_band = ORM::factory("postage_band"); - $postage_band->name = "No Postage"; - $postage_band->save(); + $db->query("CREATE TABLE IF NOT EXISTS {orders} ( + `id` int(9) NOT NULL auto_increment, + `status` int(9) DEFAULT 0, + `name` varchar(1024), + `email` varchar(1024), + `cost` DECIMAL(10,2) default 0, + `method` int(9) DEFAULT 0, + `text` TEXT NOT NULL, + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - $product = ORM::factory("product"); - $product->name = "4x6"; - $product->cost = 5; - $product->description = "4\"x6\" print"; - $product->postage_band_id = 1; - $product->save(); + $db->query("CREATE TABLE IF NOT EXISTS `ipn_messages` ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `date` int(11) NOT NULL, + `key` varchar(20) NOT NULL, + `txn_id` varchar(20) NOT NULL, + `status` varchar(20) NOT NULL, + `success` bool default false, + `text` text, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - $product = ORM::factory("product"); - $product->name = "8x10"; - $product->cost = 25; - $product->description = "8\"x10\" print"; - $product->postage_band_id = 1; - $product->save(); - $product = ORM::factory("product"); - $product->name = "8x12"; - $product->cost = 30; - $product->description = "8\"x12\" print"; - $product->postage_band_id = 1; - $product->save(); + postage_band::create("No Postage",0,0); + + product::create("4x6",5,"4\"x6\" print",1); + product::create("8x10",25,"8\"x10\" print",1); + product::create("8x12",30,"8\"x12\" print",1); + + basket::setPaymentDetails( +"

Use the following options to pay for this order.

+

Send a chequre to..

+

Visit the shop..

+

By using internet banking..

" + ); + basket::setOrderPrefix("ORDER"); + basket::setOrderCompletePage( +"

Your order number is %order_number. To pay for this order please either:

+

- Send a cheque for %total_cost to with reference %order_number..

+

- Visit the shop and quote the order %order_number..

+

- Transfer %total_cost using internet banking with reference %order_number..

+

Order will be processed as soon as payment is received. You should receive an e-mail with your order details shortly.

" + ); + basket::setOrderCompleteEmail( +"Hi %name, + +Thank you for your order the order details are below. To pay for this order please either: + +- Send a cheque for %total_cost to with reference %order_number.. +- Visit the shop and quote the order %order_number.. +- Transfer %total_cost using internet banking with reference %order_number.. + +Order will be processed as soon as payment is received. For order pick-ups please visit.. + +Order Details +------------- +%order_details + +Thanks"); + basket::setOrderCompleteEmailSubject( +"Photography Order %order_number"); + + module::set_version("basket", 4); - module::set_version("basket", 2); } static function upgrade($version) { @@ -101,12 +140,77 @@ class basket_installer `per_item` DECIMAL(10,2) default 0, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); - $postage_band = ORM::factory("postage_band"); - $postage_band->name = "No Postage"; - $postage_band->save(); + postage_band::create("No Postage",0,0); module::set_version("basket", $version = 2); } + + if ($version == 2) { + $db->query("CREATE TABLE IF NOT EXISTS {orders} ( + `id` int(9) NOT NULL auto_increment, + `text` TEXT NOT NULL, + PRIMARY KEY (`id`)) + ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + basket::setPaymentDetails( +"

Use the following options to pay for this order.

+

Send a chequre to..

+

Visit the shop..

+

By using internet banking..

" + ); + basket::setOrderPrefix("ORDER"); + basket::setOrderCompletePage( +"

Your order number is %order_number. To pay for this order please either:

+

- Send a cheque for %total_cost to with reference %order_number..

+

- Visit the shop and quote the order %order_number..

+

- Transfer %total_cost using internet banking with reference %order_number..

+

Order will be processed as soon as payment is received. You should receive an e-mail with your order details shortly.

" + ); + basket::setOrderCompleteEmail( +"Hi %name, + +Thank you for your order the order details are below. To pay for this order please either: + +- Send a cheque for %total_cost to with reference %order_number.. +- Visit the shop and quote the order %order_number.. +- Transfer %total_cost using internet banking with reference %order_number.. + +Order will be processed as soon as payment is received. For order pick-ups please visit.. + +Order Details +------------- +%order_details + +Thanks"); + basket::setOrderCompleteEmailSubject( +"Photography Order %order_number"); + + module::set_version("basket", $version = 3); + } + + if ($version ==3 ){ + $db->query("ALTER TABLE {orders} ADD COLUMN `status` int(9) DEFAULT 0;"); + + $db->query("CREATE TABLE IF NOT EXISTS {ipn_messages} ( + `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, + `date` int(11) NOT NULL, + `key` varchar(20) NOT NULL, + `txn_id` varchar(20) NOT NULL, + `status` varchar(20) NOT NULL, + `success` bool default false, + `text` text, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8;"); + module::set_version("basket", $version = 4); + + } + + if ($version==4){ + $db->query("ALTER TABLE {orders} ADD COLUMN `name` varchar(1024);"); + $db->query("ALTER TABLE {orders} ADD COLUMN `email` varchar(1024);"); + $db->query("ALTER TABLE {orders} ADD COLUMN `method` int(9) DEFAULT 0;"); + $db->query("ALTER TABLE {orders} ADD COLUMN `cost` DECIMAL(10,2) default 0"); + module::set_version("basket", $version = 5); + } } static function uninstall(){ @@ -115,5 +219,6 @@ class basket_installer $db->query("DROP TABLE IF EXISTS {product_overrides}"); $db->query("DROP TABLE IF EXISTS {item_products}"); $db->query("DROP TABLE IF EXISTS {postage_bands}"); + $db->query("DROP TABLE IF EXISTS {orders}"); } } diff --git a/modules/basket/helpers/basket_theme.php b/modules/basket/helpers/basket_theme.php index 2c9ae671..1ad535e5 100644 --- a/modules/basket/helpers/basket_theme.php +++ b/modules/basket/helpers/basket_theme.php @@ -1,7 +1,7 @@ basket = Session_Basket::get(); - return $view->render(); + if (!basket::is_side_bar_only()) + { + $view = new View("basket.html"); + + $view->basket = Session_Basket::get(); + return $view->render(); + } + return ""; } static function admin_head($theme) { @@ -36,12 +41,15 @@ class basket_theme_Core { } } static function photo_top($theme){ - if ( product::isForSale($theme->item()->id)){ - $view = new View("add_to_basket.html"); + if (!basket::is_side_bar_only()) + { + if ( product::isForSale($theme->item()->id)){ + $view = new View("add_to_basket.html"); - $view->item = $theme->item(); + $view->item = $theme->item(); - return $view->render(); + return $view->render(); + } } return ""; } diff --git a/modules/basket/helpers/postage_band.php b/modules/basket/helpers/postage_band.php index 042215cb..ca679d53 100644 --- a/modules/basket/helpers/postage_band.php +++ b/modules/basket/helpers/postage_band.php @@ -1,7 +1,7 @@ where("name", "=", $name)->find(); + if ($postage->loaded()) { + throw new Exception("@todo postage already EXISTS $name"); + } + + $postage->name = $name; + $postage->flat_rate = $flatrate; + $postage->per_item = $peritemcost; + + $postage->save(); + return $postage; + } + /** * returns the array of postage bands * @return an array of postage bands diff --git a/modules/basket/helpers/product.php b/modules/basket/helpers/product.php index 3ac44884..a4a13c8c 100644 --- a/modules/basket/helpers/product.php +++ b/modules/basket/helpers/product.php @@ -1,7 +1,7 @@ "g-add-product-form")); + $form = new Forge("admin/product_lines/add_product", "", "post", array("id" => "gAddProductForm")); $group = $form->group("add_product")->label(t("Add Product")); - $group->input("name")->label(t("Name"))->id("g-product-name") + $group->input("name")->label(t("Name"))->id("gProductName") ->error_messages("in_use", t("There is already a product with that name")); $group->input("cost")->label(t("Cost"))->id("gCost"); - $group->input("description")->label(t("Description"))->id("g-description"); + $group->input("description")->label(t("Description"))->id("gDescription"); $group->dropdown("postage_band") ->label(t("Postage Band")) ->options(postage_band::getPostageArray()); $group->submit("")->value(t("Add Product")); + $product = ORM::factory("product"); return $form; } static function get_edit_form_admin($product) { + $form = new Forge("admin/product_lines/edit_product/$product->id", "", "post", - array("id" => "g-edit-product-form")); + array("id" => "gEditProductForm")); $group = $form->group("edit_product")->label(t("Edit Product")); - $group->input("name")->label(t("Name"))->id("g-product-name")->value($product->name); + $group->input("name")->label(t("Name"))->id("gProductName")->value($product->name); $group->inputs["name"]->error_messages( "in_use", t("There is already a product with that name")); - $group->input("cost")->label(t("Cost"))->id("g-cost")->value($product->cost); - $group->input("description")->label(t("Description"))->id("g-description")-> + $group->input("cost")->label(t("Cost"))->id("gCost")->value($product->cost); + $group->input("description")->label(t("Description"))->id("gDescription")-> value($product->description); $group->dropdown("postage_band") ->label(t("Postage Band")) @@ -55,17 +57,39 @@ class product_Core { static function get_delete_form_admin($product) { $form = new Forge("admin/product_lines/delete_product/$product->id", "", "post", - array("id" => "g-delete-product-form")); + array("id" => "gDeleteProductForm")); $group = $form->group("delete_product")->label( t("Are you sure you want to delete product %name?", array("name" => $product->name))); $group->submit("")->value(t("Delete product %name", array("name" => $product->name))); return $form; } + /** + * Create a new product + * + * @param string $name + * @param string $full_name + * @param string $password + * @return User_Model + */ + static function create($name, $cost, $description, $postage_band) { + $product = ORM::factory("product")->where("name", "=", $name)->find(); + if ($product->loaded()) { + throw new Exception("@todo USER_ALREADY_EXISTS $name"); + } + + $product->name = $name; + $product->cost = $cost; + $product->description = $description; + $product->postage_band_id = $postage_band; + $product->save(); + return $product; + } + static function getProductArray($id){ $producta = array(); // check for product override - $product_override = ORM::factory("product_override")->where('item_id', "=", $id)->find(); + $product_override = ORM::factory("product_override")->where('item_id', "=", $id)->find(); if (!$product_override->loaded()){ // no override found so check parents @@ -75,11 +99,12 @@ class product_Core { $parents = $item->parents(); foreach ($parents as $parent){ // check for product override - $product_override = ORM::factory("product_override")->where('item_id', "=", $parent->id)->find(); - if ($product_override->loaded()){ - break; + $temp_override = ORM::factory("product_override")->where('item_id', "=", $parent->id)->find(); + if ($temp_override ->loaded()){ + $product_override = $temp_override; + //break; } - } + } } $products = ORM::factory("product")->find_all(); @@ -89,8 +114,8 @@ class product_Core { if ($product_override->loaded()){ $show = !$product_override->none; $item_product = ORM::factory("item_product") - ->where('product_override_id', "=", $product_override->id) - ->where('product_id', "=", $product->id)->find(); + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); if ($item_product->loaded()){ $cost = $item_product->cost; @@ -102,7 +127,7 @@ class product_Core { if ($show) { - $producta[$product->id] = $product->description." (".basket::formatMoney($cost).")"; + $producta[$product->id] = html::clean($product->description)." (".basket::formatMoneyForWeb($cost).")"; } } @@ -111,6 +136,8 @@ class product_Core { static function isForSale($id){ + try + { // check for product override $product_override = ORM::factory("product_override")->where('item_id', "=", $id)->find(); @@ -122,9 +149,10 @@ class product_Core { $parents = $item->parents(); foreach ($parents as $parent){ // check for product override - $product_override = ORM::factory("product_override")->where('item_id', "=", $parent->id)->find(); - if ($product_override->loaded()){ - break; + $temp_override = ORM::factory("product_override")->where('item_id', "=", $parent->id)->find(); + if ($temp_override ->loaded()){ + $product_override = $temp_override; + //break; } } } @@ -136,8 +164,8 @@ class product_Core { foreach ($products as $product){ $item_product = ORM::factory("item_product") - ->where('product_override_id', "=", $product_override->id) - ->where('product_id', "=", $product->id)->find(); + ->where('product_override_id', "=", $product_override->id) + ->where('product_id', "=", $product->id)->find(); if ($item_product->loaded()){ @@ -152,5 +180,10 @@ class product_Core { } else { return count($products) > 0; } + } + catch (Exception $e) + { + echo $e; + } } } \ No newline at end of file diff --git a/modules/basket/libraries/Paypal.php b/modules/basket/libraries/Paypal.php new file mode 100644 index 00000000..c18ee058 --- /dev/null +++ b/modules/basket/libraries/Paypal.php @@ -0,0 +1,330 @@ +add_field('business', 'somebody@domain.com'); + * $p->add_field('first_name', $_POST['first_name']); + * ... (add all your fields in the same manor) + * $p->submit_paypal_post(); + * + * To process an IPN, have your IPN processing file contain: + * + * $p = new paypal_class; + * if ($p->validate_ipn()) { + * ... (IPN is verified. Details are in the ipn_data() array) + * } + * + * + * In case you are new to paypal, here is some information to help you: + * + * 1. Download and read the Merchant User Manual and Integration Guide from + * http://www.paypal.com/en_US/pdf/integration_guide.pdf. This gives + * you all the information you need including the fields you can pass to + * paypal (using add_field() with this class) aswell as all the fields + * that are returned in an IPN post (stored in the ipn_data() array in + * this class). It also diagrams the entire transaction process. + * + * 2. Create a "sandbox" account for a buyer and a seller. This is just + * a test account(s) that allow you to test your site from both the + * seller and buyer perspective. The instructions for this is available + * at https://developer.paypal.com/ as well as a great forum where you + * can ask all your paypal integration questions. Make sure you follow + * all the directions in setting up a sandbox test environment, including + * the addition of fake bank accounts and credit cards. + * + ******************************************************************************* + */ + +class Paypal_Core { + + var $last_error; // holds the last error encountered + + var $ipn_response; // holds the IPN response from paypal + public $ipn_data = array(); // array contains the POST values for IPN + + var $fields = array(); // array holds the fields to submit to paypal + + + public function __construct() + { + // initialization constructor. Called when class is created. + + // sandbox paypal + + //$this->paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr"; + //$this->secure_url = "ssl://www.sandbox.paypal.com"; + + // normal paypal + $this->paypal_url = "https://www.paypal.com/cgi-bin/webscr"; + $this->secure_url = "ssl://www.paypal.com"; + + $this->last_error = ''; + + //$this->ipn_log_file = Kohana::log_directory().Kohana::config('paypal.ipn_logfile'); + //$this->ipn_log = true; + $this->ipn_response = ''; + + // populate $fields array with a few default values. See the paypal + // documentation for a list of fields and their data types. These defaul + // values can be overwritten by the calling script. + + + } + + function add_field($field, $value) { + + // adds a key=>value pair to the fields array, which is what will be + // sent to paypal as POST variables. If the value is already in the + // array, it will be overwritten. + + $this->fields["$field"] = $value; + } + + public function process($session_basket, $return_url, $cancel_url, $notify_url){ + + $this->add_field('rm','2'); + $this->add_field('cmd','_cart'); + $this->add_field('upload','1'); + + $this->add_field('currency_code', basket::getCurrency()); + $this->add_field('business', basket::getPaypalAccount()); + + // IPN stuff + $this->add_field('return', $return_url); + $this->add_field('cancel_return', $cancel_url); + $this->add_field('notify_url', $notify_url); + + // postage + if ($session_basket->ispp()){ + $postage = $session_basket->postage_cost(); + if ($postage > 0) { + $this->add_field('shipping_1',$postage); + } + } + + // basket contents + $id = 1; + foreach ($session_basket->contents as $key => $basket_item){ + $this->add_field("item_name_$id", $basket_item->getCode()); + $this->add_field("amount_$id", $basket_item->cost_per); + $this->add_field("quantity_$id",$basket_item->quantity); + $id++; + } + + // shipping address + $this->add_field("payer_email", $session_basket->email); + $this->add_field("address_name", $session_basket->name); + $this->add_field("address_street", $session_basket->house." ".$session_basket->street); + $this->add_field("address_city", $session_basket->town); + $this->add_field("address_zip", $session_basket->postcode); + $this->add_field("contact_phone", $session_basket->phone); + + $string = "
paypal_url."\">\n"; + + foreach ($this->fields as $name => $value) { + $string = $string."\n"; + } + + $string = $string."
"; + return $string; + } + + function validate_ipn($key) { + + // parse the paypal URL + $url_parsed=parse_url($this->paypal_url); + + // generate the post string from the _POST vars aswell as load the + // _POST vars into an arry so we can play with them from the calling + // script. + $post_string = 'cmd=_notify-validate'; + foreach ($_POST as $field=>$value) { + $this->ipn_data["$field"] = $value; + $value = urlencode(stripslashes($value)); + $value = preg_replace('/(.*[^%^0^D])(%0A)(.*)/i','${1}%0D%0A${3}',$value); + $post_string .= '&'.$field.'='.$value; + } + + // open the connection to paypal + + $fp = fsockopen($this->secure_url,443,$err_num,$err_str,30); + if(!$fp) { + + // could not open the connection. If loggin is on, the error message + // will be in the log. + $this->last_error = "fsockopen error no. $errnum: $errstr"; + $this->log_ipn_results($key,false); + return false; + + } else { + + // Post the data back to paypal + fputs($fp, "POST ".$url_parsed['path']." HTTP/1.1\r\n"); + fputs($fp, "Host: ".$url_parsed['host']."\r\n"); + fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n"); + + fputs($fp, "Content-length: ".strlen($post_string)."\r\n\r\n"); + //fputs($fp, "Connection: close\r\n\r\n"); + fputs($fp, $post_string . "\r\n\r\n"); + + // loop through the response from the server and append to variable + while(!feof($fp)) { + $this->ipn_response .= fgets($fp, 1024); + } + + fclose($fp); // close connection + + } + + if (stristr($this->ipn_response,"VERIFIED")===false) + { + // Invalid IPN transaction. Check the log for details. + $this->last_error = 'IPN Validation Failed. '.$url_parsed['host'].'\\'.$url_parsed['path']; + $this->log_ipn_results($key,false); + return false; + } + else{ + + // Valid IPN transaction. + + // check recievers e-mail + $business = basket::getPaypalAccount(); + + if ($this->ipn_data['receiver_email']!=$business) + { + $this->last_error = 'receivers e-mail did not match '.$business; + $this->log_ipn_results($key,false); + return false; + } + + // if confirmed check message has not been received already + if ($this->ipn_data['payment_status'] == "Completed"){ + + $message = ORM::factory("ipn_message") + ->where('key',"=",$key) + ->where('status',"=",'completed') + ->where('txn_id',"=",$this->ipn_data['txn_id'])->find(); + + if ($message->loaded()){ + $this->last_error = 'Message alread received.'; + $this->log_ipn_results($key,false); + return false; + } + } + + $this->log_ipn_results($key,true); + return true; + + } + + } + + function log_ipn_results($key, $success) { + + // Timestamp + $text = '['.date('m/d/Y g:i A').'] - '; + + $message = ORM::factory("ipn_message"); + $message->date = time(); + $message->key = $key; + $message->txn_id = $this->ipn_data['txn_id']; + $message->status = $this->ipn_data['payment_status']; + $message->success = $success; + + // Success or failure being logged? + if ($success) $text .= "SUCCESS!\n"; + else $text .= 'FAIL: '.$this->last_error."\n"; + + // Log the POST variables + $text .= "IPN POST Vars from Paypal:\n"; + foreach ($this->ipn_data as $key=>$value) { + $text .= "$key=$value \n"; + } + + // Log the response from the paypal server + $text .= "\nIPN Response from Paypal Server:\n ".$this->ipn_response; + + $message->text = $text; + $message->save(); + } + + function dump_fields() { + + // Used for debugging, this function will output all the field/value pairs + // that are currently defined in the instance of the class using the + // add_field() function. + + echo "

paypal_class->dump_fields() Output:

"; + echo " + + + + "; + + ksort($this->fields); + foreach ($this->fields as $key => $value) { + echo ""; + } + + echo "
Field NameValue
$key".urldecode($value)." 

"; + } +} + + + diff --git a/modules/basket/libraries/Session_Basket.php b/modules/basket/libraries/Session_Basket.php index ba32442b..c73cfe36 100644 --- a/modules/basket/libraries/Session_Basket.php +++ b/modules/basket/libraries/Session_Basket.php @@ -1,7 +1,7 @@ contents)){ foreach ($this->contents as $key => $item){ unset($this->contents[$key]); } } + $this->ppenabled = true; } + public function enablepp() + { + $this->ppenabled = true; + } + + public function disablepp() + { + $this->ppenabled = false; + } + + public function ispp(){ + return $this->ppenabled; + } + + private function create_key($product, $id){ return "$product _ $id"; } @@ -116,7 +134,7 @@ class Session_Basket_Core { $key = $this->create_key($product, $id); if (isset($this->contents[$key])){ - $this->contents[$key]->add($id, $quantity); + $this->contents[$key]->add($quantity); } else { $this->contents[$key] = new basket_item($product, $id, $quantity); diff --git a/modules/basket/models/ipn_message.php b/modules/basket/models/ipn_message.php new file mode 100644 index 00000000..c6f75ffa --- /dev/null +++ b/modules/basket/models/ipn_message.php @@ -0,0 +1,16 @@ +date); + } + + public function json_encode(){ + $toReturn = array( + 'id' => $this->id, + 'date' => $this->formatedTime(), + 'text' => text::convertText($this->text)); + return $toReturn; + } +} \ No newline at end of file diff --git a/modules/basket/models/item_product.php b/modules/basket/models/item_product.php index 9dd24218..e152ab19 100644 --- a/modules/basket/models/item_product.php +++ b/modules/basket/models/item_product.php @@ -1,7 +1,7 @@ -did." ".$this->name." ".$this->status(); + } + + public function status(){ + switch ($this->status){ + case 1: + return "Waiting Payment"; + case 2: + return "Payment Confirmed"; + case 20: + return "Complete"; + + default: + return "Unknown"; + } + } + + public function payment_method(){ + switch ($this->method){ + case 1: + return "through Paypal"; + case 2: + return "offline"; + + default: + return "Unknown"; + } + } +} diff --git a/modules/basket/models/postage_band.php b/modules/basket/models/postage_band.php index e4354f9c..83eb6e08 100644 --- a/modules/basket/models/postage_band.php +++ b/modules/basket/models/postage_band.php @@ -1,7 +1,7 @@ "length[1,32]"); - /** - * Specify our rules here so that we have access to the instance of this model. - */ - public function validate($array=null) { - if (!$array) { - $this->rules = array( - "name" => array("rules" => array("required", "length[1,32]"), - "callbacks" => array(array($this, "valid_name"))), - "flat_rate" => array("rules" => array("required", "decimal")), - "per_item" => array("rules" => array("required"))); + protected $has_many=array('products'); - } - - parent::validate($array); - } - - /** - * Validate the item name. It can't conflict with other names, can't contain slashes or - * trailing periods. - */ - public function valid_name(Validation $v, $field) { - $postage_band = ORM::factory("postage_band")->where("name", "=", $this->name)->find(); - if ($postage_band->loaded() && $postage_band->id != $this->id) { - $v->add_error("name", "in_use"); - } - } } diff --git a/modules/basket/models/product.php b/modules/basket/models/product.php index d215aa39..2bd6a59b 100644 --- a/modules/basket/models/product.php +++ b/modules/basket/models/product.php @@ -1,7 +1,7 @@ "length[1,32]", + "description" => "length[0,255]"); + protected $belongs_to=array('postage_band'); - /** - * Specify our rules here so that we have access to the instance of this model. - */ - public function validate($array=null) { - if (!$array) { - $this->rules = array( - "name" => array("rules" => array("required", "length[1,32]"), - "callbacks" => array(array($this, "valid_name"))), - "description" => array("rules" => array("required", "length[0,255]")), - "cost" => array("rules" => array("required", "decimal"))); - } - - parent::validate($array); - } - - /** - * Validate the item name. It can't conflict with other names, can't contain slashes or - * trailing periods. - */ - public function valid_name(Validation $v, $field) { - Kohana_Log::add("error",print_r("valid_name!",1)); - $product = ORM::factory("product")->where("name", "=", $this->name)->find(); - if ($product->loaded() && $product->id != $this->id) { - $v->add_error("name", "in_use"); - } - } } diff --git a/modules/basket/models/product_override.php b/modules/basket/models/product_override.php index e6b28a0c..e3e720fd 100644 --- a/modules/basket/models/product_override.php +++ b/modules/basket/models/product_override.php @@ -1,7 +1,7 @@ - \ No newline at end of file diff --git a/modules/basket/views/admin_configure.html.php b/modules/basket/views/admin_configure.html.php index 7ee7f09e..9960fe01 100644 --- a/modules/basket/views/admin_configure.html.php +++ b/modules/basket/views/admin_configure.html.php @@ -1,8 +1,7 @@
-

-

- -

- +

+

+

+
\ No newline at end of file diff --git a/modules/basket/views/admin_postage_bands.html.php b/modules/basket/views/admin_postage_bands.html.php index f52cd165..d79fe380 100644 --- a/modules/basket/views/admin_postage_bands.html.php +++ b/modules/basket/views/admin_postage_bands.html.php @@ -1,7 +1,7 @@ " - class="g-dialog-link g-button-link right ui-icon-left ui-state-default ui-corner-all" - title=""> + class="g-dialog-link g-button right ui-icon-left ui-state-default ui-corner-all" + title=""> @@ -31,44 +31,41 @@ - - + \ No newline at end of file diff --git a/modules/basket/views/admin_product_lines.html.php b/modules/basket/views/admin_product_lines.html.php index f1073211..f20f2a37 100644 --- a/modules/basket/views/admin_product_lines.html.php +++ b/modules/basket/views/admin_product_lines.html.php @@ -1,7 +1,7 @@ " - class="g-dialog-link g-button g-right ui-icon-left ui-state-default ui-corner-all" + class="g-dialog-link g-button right ui-icon-left ui-state-default ui-corner-all" title=""> @@ -42,12 +42,12 @@ $product): ?> - "> + "> name) ?> - cost) ?> + cost) ?> description) ?> @@ -57,11 +57,11 @@ - + id") ?>" open_text="" class="g-panel-link g-button ui-state-default ui-corner-all ui-icon-left"> - + id") ?>" class="g-dialog-link g-button ui-state-default ui-corner-all ui-icon-left"> diff --git a/modules/basket/views/admin_templates.html.php b/modules/basket/views/admin_templates.html.php new file mode 100644 index 00000000..263293c4 --- /dev/null +++ b/modules/basket/views/admin_templates.html.php @@ -0,0 +1,8 @@ + +
+

+

+ +

+ +
\ No newline at end of file diff --git a/modules/basket/views/basket-side-bar.html.php b/modules/basket/views/basket-side-bar.html.php new file mode 100644 index 00000000..d6ac00d7 --- /dev/null +++ b/modules/basket/views/basket-side-bar.html.php @@ -0,0 +1,42 @@ +page_type != 'basket'){ + if (basket::can_view_orders()){ + ?>
" title="">item(); + if ($item->is_photo() && product::isForSale($theme->item()->id)){ + ?>

+id") ?>" +title="">

+contents) && ($basket->size() > 0)) { + ?>
+

" title="">

- page_subtype != 'basket'): ?> + + page_type != 'basket'): ?> + + " + title="">View Orders + contents) && ($basket->size() > 0)): ?>
" diff --git a/modules/basket/views/checkout.html.php b/modules/basket/views/checkout.html.php index 395847e6..b9ceaa2d 100644 --- a/modules/basket/views/checkout.html.php +++ b/modules/basket/views/checkout.html.php @@ -1,7 +1,7 @@ diff --git a/modules/basket/views/confirm_order.html.php b/modules/basket/views/confirm_order.html.php index 2599041d..60ff14ac 100644 --- a/modules/basket/views/confirm_order.html.php +++ b/modules/basket/views/confirm_order.html.php @@ -1,7 +1,7 @@ -
+

Basket Summary

-
+
@@ -33,7 +33,8 @@ function so(){document.confirm.submit();} contents as $key => $prod_details): ?> - "> + "> + - + postage_cost();?> 0):?> - "> - + "> + - "> - + "> + +
getItem(); ?>
@@ -47,19 +48,20 @@ function so(){document.confirm.submit();} quantity) ?>
- cost)) ?> + cost) ?>
Postage and Packaging
ispp()?"":"style=\"text-decoration:line-through\""; ?>>Postage and Packagingispp()?"":"style=\"text-decoration:line-through\""; ?>>
Total Costcost() + $postage))?>
Total Costispp()?basket::formatMoneyForWeb($basket->cost() + $postage):basket::formatMoneyForWeb($basket->cost()); ?>
@@ -78,9 +80,10 @@ E-mail : email ?>
Telephone : phone ?>
-" class="g-left g-button ui-state-default ui-corner-all ui-icon-left"> +
diff --git a/modules/basket/views/order_complete.html.php b/modules/basket/views/order_complete.html.php index b38151e7..5f384732 100644 --- a/modules/basket/views/order_complete.html.php +++ b/modules/basket/views/order_complete.html.php @@ -1,7 +1,7 @@
+

Thankyou for your order

-You will be contacted soon to arrange payment and delivery. +method == Order_Model::PAYMENT_PAYPAL){ +?>Your order will be confirmed when Paypal has finished processing your order.$ordernumber, "total_cost"=>basket::formatMoneyForWeb($total_cost)));?>
\ No newline at end of file diff --git a/modules/basket/views/paypal_redirect.html.php b/modules/basket/views/paypal_redirect.html.php new file mode 100644 index 00000000..26a2438b --- /dev/null +++ b/modules/basket/views/paypal_redirect.html.php @@ -0,0 +1,4 @@ + +

Processing

If you are not automatically redirected to + paypal within 5 seconds Click Here.

+ diff --git a/modules/basket/views/pew1.html.php b/modules/basket/views/pew1.html.php new file mode 100644 index 00000000..c751f7cb --- /dev/null +++ b/modules/basket/views/pew1.html.php @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/modules/basket/views/pew2.html.php b/modules/basket/views/pew2.html.php new file mode 100644 index 00000000..41324d2d --- /dev/null +++ b/modules/basket/views/pew2.html.php @@ -0,0 +1,17 @@ + + \ No newline at end of file diff --git a/modules/basket/views/print_order.html.php b/modules/basket/views/print_order.html.php new file mode 100644 index 00000000..63598131 --- /dev/null +++ b/modules/basket/views/print_order.html.php @@ -0,0 +1,20 @@ + + + +Print Order + + + + + + + + + + + \ No newline at end of file diff --git a/modules/basket/views/view_basket.html.php b/modules/basket/views/view_basket.html.php index c213d4a2..891d2647 100644 --- a/modules/basket/views/view_basket.html.php +++ b/modules/basket/views/view_basket.html.php @@ -1,7 +1,7 @@ +
+
contents ) && count($basket->contents) > 0): ?> - - - - - " - class="g-right g-button ui-state-default ui-corner-all ui-icon-right"> - - - " - class="g-right g-button ui-state-default ui-corner-all ui-icon-right"> - - - -

- -

-
+ $(document).ready(function(){ + $("#pickup").click(function(){ + if (this.checked) + { + window.location = ""; + } + else + { + window.location = ""; + } + }); + }) + + + " + class="right g-button ui-state-default ui-corner-all ui-icon-right"> + + +
+
contents ) && count($basket->contents) > 0): ?> - +
@@ -58,12 +76,14 @@ contents as $key => $prod_details): ?> - "> + "> - "> - + "> + + + "> - "> - + + "> +
getItem(); ?>
+ <?= $item->title?> +
@@ -74,13 +94,9 @@ cost?> - cost)) ?> + cost); ?> - + " class="g-button ui-state-default ui-corner-all ui-icon-left"> @@ -90,34 +106,32 @@ postage_cost();?> 0):?> -
Postage and Packaging
ispp()?"":"style=\"text-decoration:line-through\""; ?>>Postage and Packagingispp()?"":"style=\"text-decoration:line-through\""; ?>> +
ispp()?"":"checked"; ?>/> Select if you wish to pick up the photos.
Total Cost
Total Costispp()?basket::formatMoneyForWeb($total + $postage):basket::formatMoneyForWeb($total)?>
+ Shopping Basket is Empty
+
\ No newline at end of file diff --git a/modules/basket/views/view_ipn.html.php b/modules/basket/views/view_ipn.html.php new file mode 100644 index 00000000..6c19e27f --- /dev/null +++ b/modules/basket/views/view_ipn.html.php @@ -0,0 +1,46 @@ + +

IPN Messages for title()?>

+">Back to orders +
+ + +
+

+
+ diff --git a/modules/basket/views/view_order.html.php b/modules/basket/views/view_order.html.php new file mode 100644 index 00000000..d735e26f --- /dev/null +++ b/modules/basket/views/view_order.html.php @@ -0,0 +1,18 @@ + +

title()?>

+Payment is payment_method()?>status==Order_Model::WAITING_PAYMENT){ + ?>
id)."?csrf=$csrf";?>">Confirm Order Payment status==Order_Model::PAYMENT_CONFIRMED){ + ?>
id)."?csrf=$csrf";?>">Confirm Order Delivery method==Order_Model::PAYMENT_PAYPAL){ + ?>
id);?>">View Paypal IPN Messages
+",$order->text);?> \ No newline at end of file diff --git a/modules/basket/views/view_orders.html.php b/modules/basket/views/view_orders.html.php new file mode 100644 index 00000000..a5c90e1c --- /dev/null +++ b/modules/basket/views/view_orders.html.php @@ -0,0 +1,67 @@ + +
+
+ + + + + + +
+
+
+ +
+

+
+ diff --git a/modules/ecard/helpers/ecard.php b/modules/ecard/helpers/ecard.php index 7fb79048..13485954 100644 --- a/modules/ecard/helpers/ecard.php +++ b/modules/ecard/helpers/ecard.php @@ -56,6 +56,7 @@ class ecard_Core { ->error_messages("required", t("You must enter a message")); $group->hidden("item_id")->value($item->id); module::event("ecard_send_form", $form); + module::event("captcha_protect_form", $form); $group->submit("")->value(t("Send"))->class("ui-state-default ui-corner-all"); return $form; diff --git a/modules/ecard/helpers/ecard_block.php b/modules/ecard/helpers/ecard_block.php index 23136d55..051c55c6 100644 --- a/modules/ecard/helpers/ecard_block.php +++ b/modules/ecard/helpers/ecard_block.php @@ -1,7 +1,7 @@ get("settings_menu") ->append(Menu::factory("link") ->id("ecard") - ->label(t("eCard Settings")) + ->label(t("eCard settings")) ->url(url::site("admin/ecard"))); } diff --git a/modules/ecard/helpers/ecard_installer.php b/modules/ecard/helpers/ecard_installer.php index 98d73702..7f70b3e9 100644 --- a/modules/ecard/helpers/ecard_installer.php +++ b/modules/ecard/helpers/ecard_installer.php @@ -25,6 +25,6 @@ class ecard_installer { "Click the image to be taken to the gallery."); module::set_var("ecard", "bcc", ""); module::set_var("ecard", "access_permissions", "everybody"); - module::set_version("ecard", 2); + module::set_version("ecard", 3); } } diff --git a/modules/ecard/module.info b/modules/ecard/module.info index 3cc27a8a..231a04fb 100644 --- a/modules/ecard/module.info +++ b/modules/ecard/module.info @@ -1,4 +1,4 @@ name = "E-Card" description = "Send a photo as a postcard" -version = 2 +version = 3 diff --git a/modules/embed_videos/controllers/embedded_videos.php b/modules/embed_videos/controllers/embedded_videos.php new file mode 100644 index 00000000..6c86e3a0 --- /dev/null +++ b/modules/embed_videos/controllers/embedded_videos.php @@ -0,0 +1,166 @@ +validate(); + if ($form->add_embedded_video->inputs['video_url']->value != "") { + $title = $form->add_embedded_video->inputs['title']->value; + $description = $form->add_embedded_video->inputs['description']->value; + $valid_url=false; + $embedded_video = ORM::factory("embedded_video"); + $item = ORM::factory("item"); + $item->type = "photo"; + $url = $form->add_embedded_video->inputs['video_url']->value; + if(preg_match("/$youtubeUrlPattern/",$url)) { + $video_id = 0; + if (preg_match("/watch\?v=(.*?)(&\S+=\S+)/",$url,$matches)) { + $video_id = $matches[1]; + } else if (preg_match("/watch\?v=(.*)/",$url,$matches)) { + $video_id = $matches[1]; + } else if (preg_match("/v\/(.*)/",$url,$matches)) { + $video_id = $matches[1]; + } + if ($video_id) { + $video_id = $matches[1]; + $embedded_video->embed_code = ''; + $embedded_video->source = "YouTube"; + $content = file_get_contents("http://img.youtube.com/vi/" . $video_id . "/0.jpg"); + $itemname = "youtube_" . $video_id . ".jpg"; + $temp_filename = VARPATH . "tmp/$itemname"; + if ($content) { + $valid_url = true; + $sxml = simplexml_load_file("http://gdata.youtube.com/feeds/api/videos/$video_id"); + if ($sxml) { + if ($title == '') { + $title = (string)$sxml->title; + } + if ($description == '') { + $description = (string)$sxml->content; + } + } + } + } + } else if(preg_match("/$vimeoUrlPattern/",$url)) { + if(preg_match("/$vimeoUrlPattern\/(.*)/",$url,$matches)) { + $video_id = $matches[1]; + if ($video_id) { + $sxml = simplexml_load_file("http://vimeo.com/api/v2/video/$video_id.xml"); + if ($sxml) { + if ($title == '') { + $title = (string)$sxml->video->title; + } + if ($description == '') { + $description = strip_tags((string)$sxml->video->description); + } + $embedded_video->source = "Vimeo"; + $content = file_get_contents((string)$sxml->video->thumbnail_large); + $itemname = "vimeo_" . $video_id . ".jpg"; + $temp_filename = VARPATH . "tmp/$itemname"; + $width = min((int)$sxml->video->width, $maxwidth); + $height = min((int)$sxml->video->height, $maxheight); + $embedded_video->embed_code = ''; + $valid_url = true; + } + } + } + } + //$item->validate(); + //$content = file_get_contents("http://img.youtube.com/vi/" . $form->add_embedded_video->inputs['name']->value . "/0.jpg"); + if ($valid_url) { + $file = fopen($temp_filename, "wb"); + fwrite($file, $content); + fclose($file); + gallery_graphics::composite($temp_filename, $temp_filename, array("file" => "modules/embed_videos/images/embed_video_icon.png", "position" => "center", "transparency" => 95)); + $item->set_data_file($temp_filename); + $item->name = basename($itemname); + $item->title = $title; + $item->parent_id = $album->id; + $item->description = $description; + $item->slug = $form->add_embedded_video->inputs['slug']->value; + $path_info = @pathinfo($temp_filename); + $item->save(); + $embedded_video->item_id = $item->id; + $embedded_video->validate(); + $embedded_video->save(); + log::success("content", t("Added a embedded video"), html::anchor("embeds/$item->id", t("view video"))); + module::event("add_event_form_completed", $item, $form); + } else { + $form->add_embedded_video->inputs['video_url']->add_error('invalid_id', 1); + $valid = false; + } + } else { + $form->add_embedded_video->inputs['video_url']->add_error('invalid_id', 1); + $valid = false; + } + } + catch(Exception $e) { + // Lame error handling for now. Just record the exception and move on + Kohana_Log::add("error", $e->getMessage() . "\n" . $e->getTraceAsString()); + // Ugh. I hate to use instanceof, But this beats catching the exception separately since + // we mostly want to treat it the same way as all other exceptions + if ($e instanceof ORM_Validation_Exception) { + Kohana_Log::add("error", "Validation errors: " . print_r($e->validation->errors(), 1)); + foreach($e->validation->errors() as $key => $error) { + $form->add_embed->inputs[$key]->add_error($error, 1); + } + $valid = false; + } + if (file_exists($temp_filename)) { + unlink($temp_filename); + } + } + if (file_exists($temp_filename)) { + unlink($temp_filename); + } + batch::stop(); + if ($valid) { + //print json_encode(array("result" => "success")); + json::reply(array("result" => "success", "location" => $item->url())); + } else { + //json::reply(array("result" => "error", "form" => (string)$form)); + print $form; + } + } + public function form_add($album_id) { + $album = ORM::factory("item", $album_id); + access::required("view", $album); + access::required("add", $album); + print embed_videos::get_add_form($album); + } +} diff --git a/modules/embed_videos/helpers/embed_videos.php b/modules/embed_videos/helpers/embed_videos.php new file mode 100644 index 00000000..75b47ec2 --- /dev/null +++ b/modules/embed_videos/helpers/embed_videos.php @@ -0,0 +1,57 @@ +id}", "", "post", array("id" => "g-add-embed-form")); + $group = $form->group("add_embedded_video") + ->label(t("Add embedded video to %album_title", array("album_title" => $album->title))); + $group->input("title")->label(t("Title")) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); + $group->input("video_url")->label(t("Video URL")) + ->error_messages( + "conflict", t("There is already a movie with this ID")) + ->error_messages("required", t("You must provide a URL")) + ->error_messages("invalid_id", t("Invalid URL")); + $group->textarea("description")->label(t("Description")); + $group->input("slug")->label(t("Internet Address")) + ->error_messages( + "conflict", t("There is already a movie, photo or album with this internet address")) + ->error_messages( + "not_url_safe", + t("The internet address should contain only letters, numbers, hyphens and underscores")) + ->error_messages("required", t("You must provide an internet address")) + ->error_messages("length", t("Your internet address is too long")); + + module::event("item_add_form", $album, $form); + + $group = $form->group("buttons")->label(""); + $group->submit("")->value(t("Add")); + + return $form; + } +} \ No newline at end of file diff --git a/modules/embed_videos/helpers/embed_videos_event.php b/modules/embed_videos/helpers/embed_videos_event.php new file mode 100644 index 00000000..8ef00064 --- /dev/null +++ b/modules/embed_videos/helpers/embed_videos_event.php @@ -0,0 +1,38 @@ +where("item_id", "=", $item->id) + ->find() + ->delete(); + } + static function site_menu($menu, $theme) { + $item = $theme->item(); + if ($can_add = $item && access::can("add", $item)) { + $menu->get("add_menu") + ->append(Menu::factory("dialog") + ->id("embed_add") + ->label(t("Embed Video")) + ->url(url::site("form/add/embedded_videos/$item->id"))); + } + } +} diff --git a/modules/embed_videos/helpers/embed_videos_installer.php b/modules/embed_videos/helpers/embed_videos_installer.php new file mode 100644 index 00000000..dd69489a --- /dev/null +++ b/modules/embed_videos/helpers/embed_videos_installer.php @@ -0,0 +1,44 @@ +query("CREATE TABLE {embedded_videos} ( + `id` int(9) NOT NULL auto_increment, + `embed_code` varchar(2048) DEFAULT NULL, + `source` varchar(64) DEFAULT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY (`item_id`, `id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("embed_videos", 2); + //exec("cd modules/gallery/controllers/; ln -s ../../embed/controllers/embeds.php embeds.php"); + } + + static function deactivate() { + + } + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {embedded_videos};"); + module::delete("embed_videos"); + } +} diff --git a/modules/embed_videos/helpers/embed_videos_theme.php b/modules/embed_videos/helpers/embed_videos_theme.php new file mode 100644 index 00000000..b7464478 --- /dev/null +++ b/modules/embed_videos/helpers/embed_videos_theme.php @@ -0,0 +1,34 @@ +item(); + if ($item && $item->is_photo()) { + $embedded_video = ORM::factory("embedded_video") + ->where("item_id", "=", $item->id) + ->find(); + if ($embedded_video->loaded()) { + $view = new View("embed_video_js.html"); + $view->embed_code = addslashes($embedded_video->embed_code); + return $view; + } + } + } +} \ No newline at end of file diff --git a/modules/embed_videos/images/embed_video_icon.png b/modules/embed_videos/images/embed_video_icon.png new file mode 100644 index 00000000..81e5a9d4 Binary files /dev/null and b/modules/embed_videos/images/embed_video_icon.png differ diff --git a/modules/embed_videos/models/embedded_video.php b/modules/embed_videos/models/embedded_video.php new file mode 100644 index 00000000..6ea97e8f --- /dev/null +++ b/modules/embed_videos/models/embedded_video.php @@ -0,0 +1,25 @@ + + + + \ No newline at end of file diff --git a/modules/gwtorganise/controllers/json_album.php b/modules/gwtorganise/controllers/json_album.php index 637df65a..5cf032f8 100644 --- a/modules/gwtorganise/controllers/json_album.php +++ b/modules/gwtorganise/controllers/json_album.php @@ -310,7 +310,7 @@ class Json_Album_Controller extends Controller { $i++; } - json::reply($toreturn); + print json_encode($toreturn); } @@ -322,7 +322,7 @@ class Json_Album_Controller extends Controller { $item = $this->p_rotate($item, $dir); - json::reply(self::child_json_encode($item)); + print json_encode(self::child_json_encode($item)); } public function resize_config(){ @@ -338,5 +338,4 @@ class Json_Album_Controller extends Controller { json::reply(array("resize" => false)); } } - -} \ No newline at end of file +} diff --git a/modules/iptc/controllers/admin_iptc.php b/modules/iptc/controllers/admin_iptc.php new file mode 100644 index 00000000..dd803951 --- /dev/null +++ b/modules/iptc/controllers/admin_iptc.php @@ -0,0 +1,84 @@ +content = new View("admin_iptc.html"); + $view->content->iptc_form = $this->_get_admin_form(); + print $view; + } + + public function saveprefs() { + // Save user preferences to the database. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Make sure the user filled out the form properly. + $form = $this->_get_admin_form(); + if ($form->validate()) { + Kohana_Log::add("error",print_r($form,1)); + + // Save settings to Gallery's database. + foreach (iptc::keys() as $keyword => $iptcvar) { + $checkbox = false; + for ($i = 0; $i < count($form->Global->$keyword); $i++) { + if ($form->Global->$keyword->value[$i] == $keyword) { + $checkbox = true; + } + } + module::set_var("iptc", "show_".$keyword, $checkbox); + } + // Display a success message and redirect back to the TagsMap admin page. + message::success(t("Your settings have been saved.")); + url::redirect("admin/iptc"); + } + + // Else show the page with errors + $view = new Admin_View("admin.html"); + $view->content = new View("admin_iptc.html"); + $view->content->iptc_form = $form; + print $view; + } + + private function _get_admin_form() { + // Make a new Form. + $form = new Forge("admin/iptc/saveprefs", "", "post", array("id" => "g-iptc-adminForm")); + + // Create group for display settings + $iptc_display_group = $form->group("Global") + ->label(t("Display Settings")); + + $show = t("Show"); + foreach (iptc::keys() as $keyword => $iptcvar) { + unset($checkbox); + $checkbox[$keyword] = array($show." \"".$iptcvar[1]."\" ?", module::get_var("iptc", "show_".$keyword)); + $iptc_display_group->checklist($keyword) + ->options($checkbox); + } + // Add a save button to the form. + $form->submit("SaveSettings")->value(t("Save")); + + // Return the newly generated form. + return $form; + } +} diff --git a/modules/iptc/helpers/iptc.php b/modules/iptc/helpers/iptc.php new file mode 100644 index 00000000..cca0f61f --- /dev/null +++ b/modules/iptc/helpers/iptc.php @@ -0,0 +1,207 @@ +is_photo() && $item->mime_type == "image/jpeg") { + $info = getJpegHeader($item->file_path()); + if ($info !== FALSE) { + $iptcBlock = getIptcBlock($info); + if ($iptcBlock !== FALSE) { + $iptc = iptcparse($iptcBlock); + } else { + $iptc = array(); + } + $xmp = getXmpDom($info); + + foreach (self::keys() as $keyword => $iptcvar) { + $iptc_key = $iptcvar[0]; + $xpath = $iptcvar[2]; + $value = null; + if ($xpath != null) { + $value = getXmpValue($xmp, $xpath); + } + if ($value == null) { + if (!empty($iptc[$iptc_key])) { + $value = implode(";", $iptc[$iptc_key]); + if (function_exists("mb_detect_encoding") && mb_detect_encoding($value) != "UTF-8") { + $value = utf8_encode($value); + } + } + } + if ($value != null) { + $keys[$keyword] = Input::clean($value); + } + } + } + } + + $record = ORM::factory("iptc_record")->where("item_id", "=", $item->id)->find(); + if (!$record->loaded()) { + $record->item_id = $item->id; + } + $record->data = serialize($keys); + $record->key_count = count($keys); + $record->dirty = 0; + $record->save(); + } + + static function get($item) { + $iptc = array(); + $record = ORM::factory("iptc_record") + ->where("item_id", "=", $item->id) + ->find(); + if (!$record->loaded()) { + return array(); + } + + $definitions = self::keys(); + $keys = unserialize($record->data); + foreach ($keys as $key => $value) { + if (module::get_var("iptc", "show_".$key) == 1) + $iptc[] = array("caption" => $definitions[$key][1], "value" => $value); + } + + return $iptc; + } + + + public static function keys() { + if (!isset(self::$iptc_keys)) { + self::$iptc_keys = array( + "ObjectName" => array("2#005", + t("IPTC Object Name"), + "/x:xmpmeta/rdf:RDF/rdf:Description/dc:title/rdf:Alt/rdf:li" ), + "EditStatus" => array("2#007", + t("IPTC Edit Status"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@mediapro:Status" ), + "Category" => array("2#015", + t("IPTC Category"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:Category" ), + "SupplementalCategories" => array("2#020", + t("IPTC Categories"), + "/x:xmpmeta/rdf:RDF/rdf:Description/photoshop:SupplementalCategories/rdf:Bag/rdf:li" ), + "FixtureIdentifier" => array("2#022", + t("IPTC Identifier"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@mediapro:Event" ), + "Keywords" => array("2#025", + t("IPTC Keywords"), + "/x:xmpmeta/rdf:RDF/rdf:Description/dc:subject/rdf:Bag/rdf:li" ), + "LocationCode" => array("2#026", + t("IPTC Location Code"), + null ), + "SpecialInstructions" => array("2#040", + t("IPTC Instructions"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:Instructions" ), + "DateCreated" => array("2#055", + t("IPTC Created Date"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:DateCreated" ), + "ByLine" => array("2#080", + t("IPTC Author"), + "/x:xmpmeta/rdf:RDF/rdf:Description/dc:creator/rdf:Seq/rdf:li" ), + "ByLineTitle" => array("2#085", + t("IPTC Author Title"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:AuthorsPosition" ), + "City" => array("2#090", + t("IPTC City"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:City" ), + "SubLocation" => array("2#092", + t("IPTC SubLocation"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@Iptc4xmpCore:Location | /x:xmpmeta/rdf:RDF/rdf:Description/@mediapro:Location" ), + "ProvinceState" => array("2#095", + t("IPTC Province State"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:State" ), + "CountryCode" => array("2#100", + t("IPTC Country Code"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@Iptc4xmpCore:CountryCode" ), + "CountryName" => array("2#101", + t("IPTC Country Name"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:Country" ), + "Transmission" => array("2#103", + t("IPTC Transmission,"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:TransmissionReference" ), + "HeadLine" => array("2#105", + t("IPTC HeadLine"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:Headline" ), + "Credit" => array("2#110", + t("IPTC Credit"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:Credit | /x:xmpmeta/rdf:RDF/rdf:Description/photoshop:Credit" ), + "Source" => array("2#115", + t("IPTC Source"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:Source" ), + "Copyright" => array("2#116", + t("IPTC Copyright"), + "/x:xmpmeta/rdf:RDF/rdf:Description/dc:rights/rdf:Alt/rdf:li" ), + "Contacts" => array("2#118", + t("IPTC Contacts"), + "/x:xmpmeta/rdf:RDF/rdf:Description/mediapro:People/rdf:Bag/rdf:li" ), + "Caption" => array("2#120", + t("IPTC Caption"), + "/x:xmpmeta/rdf:RDF/rdf:Description/dc:description/rdf:Alt/rdf:li" ), + "Redactor" => array("2#122", + t("IPTC Redactor"), + "/x:xmpmeta/rdf:RDF/rdf:Description/@photoshop:CaptionWriter" ) + ); + } + return self::$iptc_keys; + } + + + static function stats() { + $missing_iptc = db::build() + ->select("items.id") + ->from("items") + ->join("iptc_records", "items.id", "iptc_records.item_id", "left") + ->where("type", "=", "photo") + ->and_open() + ->where("iptc_records.item_id", "IS", null) + ->or_where("iptc_records.dirty", "=", 1) + ->close() + ->execute() + ->count(); + + $total_items = ORM::factory("item")->where("type", "=", "photo")->count_all(); + if (!$total_items) { + return array(0, 0, 0); + } + return array($missing_iptc, $total_items, + round(100 * (($total_items - $missing_iptc) / $total_items))); + } + + static function check_index() { + list ($remaining) = iptc::stats(); + if ($remaining) { + site_status::warning( + t('Your Iptc index needs to be updated. Fix this now', + array("url" => html::mark_clean(url::site("admin/maintenance/start/iptc_task::update_index?csrf=__CSRF__")))), + "iptc_index_out_of_date"); + } + } +} diff --git a/modules/iptc/helpers/iptc_block.php b/modules/iptc/helpers/iptc_block.php new file mode 100644 index 00000000..1a6ed955 --- /dev/null +++ b/modules/iptc/helpers/iptc_block.php @@ -0,0 +1,43 @@ + t("IPTC info")); + } + + static function get($block_id, $theme) { + $block = ""; + switch ($block_id) { + case "iptc": + if ($theme->item()) { + $details = iptc::get($theme->item()); + if (count($details) > 0) { + $block = new Block(); + $block->css_id = "g-metadata"; + $block->title = t("IPTC info"); + $block->content = new View("iptc_block.html"); + $block->content->details = $details; + } + } + break; + } + return $block; + } +} \ No newline at end of file diff --git a/modules/iptc/helpers/iptc_event.php b/modules/iptc/helpers/iptc_event.php new file mode 100644 index 00000000..c7b4a6cc --- /dev/null +++ b/modules/iptc/helpers/iptc_event.php @@ -0,0 +1,42 @@ +is_photo()) { + iptc::extract($item); + } + } + + static function item_deleted($item) { + db::build() + ->delete("iptc_records") + ->where("item_id", "=", $item->id) + ->execute(); + } + + static function admin_menu($menu, $theme) { + // Add a link to the admin page to the Settings menu. + $menu->get("settings_menu") + ->append(Menu::factory("link") + ->id("iptc") + ->label(t("IPTC Settings")) + ->url(url::site("admin/iptc"))); + } +} diff --git a/modules/iptc/helpers/iptc_installer.php b/modules/iptc/helpers/iptc_installer.php new file mode 100644 index 00000000..11a17a56 --- /dev/null +++ b/modules/iptc/helpers/iptc_installer.php @@ -0,0 +1,46 @@ +query("CREATE TABLE IF NOT EXISTS {iptc_records} ( + `id` int(9) NOT NULL auto_increment, + `item_id` INTEGER(9) NOT NULL, + `key_count` INTEGER(9) default 0, + `data` TEXT, + `dirty` BOOLEAN default 1, + PRIMARY KEY (`id`), + KEY(`item_id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("iptc", 1); + } + + static function activate() { + iptc::check_index(); + } + + static function deactivate() { + site_status::clear("iptc_index_out_of_date"); + } + + static function uninstall() { + Database::instance()->query("DROP TABLE IF EXISTS {iptc_records};"); + } +} diff --git a/modules/iptc/helpers/iptc_task.php b/modules/iptc/helpers/iptc_task.php new file mode 100644 index 00000000..d4715bff --- /dev/null +++ b/modules/iptc/helpers/iptc_task.php @@ -0,0 +1,88 @@ +delete("iptc_records") + ->where("item_id", "NOT IN", + db::build()->select("id")->from("items")->where("type", "=", "photo")) + ->execute(); + + list ($remaining, $total, $percent) = iptc::stats(); + return array(Task_Definition::factory() + ->callback("iptc_task::update_index") + ->name(t("Extract Iptc data")) + ->description($remaining + ? t2("1 photo needs to be scanned", + "%count (%percent%) of your photos need to be scanned", + $remaining, array("percent" => (100 - $percent))) + : t("IPTC data is up-to-date")) + ->severity($remaining ? log::WARNING : log::SUCCESS)); + } + + static function update_index($task) { + try { + $completed = $task->get("completed", 0); + + $start = microtime(true); + foreach (ORM::factory("item") + ->join("iptc_records", "items.id", "iptc_records.item_id", "left") + ->where("type", "=", "photo") + ->and_open() + ->where("iptc_records.item_id", "IS", null) + ->or_where("iptc_records.dirty", "=", 1) + ->close() + ->find_all() as $item) { + // The query above can take a long time, so start the timer after its done + // to give ourselves a little time to actually process rows. + if (!isset($start)) { + $start = microtime(true); + } + + iptc::extract($item); + $completed++; + + if (microtime(true) - $start > 1.5) { + break; + } + } + + list ($remaining, $total, $percent) = iptc::stats(); + $task->set("completed", $completed); + if ($remaining == 0 || !($remaining + $completed)) { + $task->done = true; + $task->state = "success"; + site_status::clear("iptc_index_out_of_date"); + $task->percent_complete = 100; + } else { + $task->percent_complete = round(100 * $completed / ($remaining + $completed)); + } + $task->status = t2("one record updated, index is %percent% up-to-date", + "%count records updated, index is %percent% up-to-date", + $completed, array("percent" => $percent)); + } catch (Exception $e) { + $task->done = true; + $task->state = "error"; + $task->status = $e->getMessage(); + $task->log((string)$e); + } + } +} diff --git a/modules/iptc/lib/functions.php b/modules/iptc/lib/functions.php new file mode 100644 index 00000000..91ca129f --- /dev/null +++ b/modules/iptc/lib/functions.php @@ -0,0 +1,97 @@ + 0xD7) { + $size = fread($file, 2); + if ($size === FALSE) { + fclose($file); + return $result; + } + $sizeOfSegment = unpack("nV", $size); + $data = fread($file, $sizeOfSegment['V']-2); + if ($data === FALSE) { + fclose($file); + return $result; + } + if ($result === FALSE) + unset($result); + $result[] = array("type" => $typeOfSegment, "data" => $data); // Multiple segments can have the same type like Exif and XMP + } + } while (!feof($file)); + fclose($file); + return $result; +} + + +function getIptcBlock($jpegHeader) +{ + for ($i = 0; $i < count($jpegHeader); $i++) { + if ($jpegHeader[$i]['type'] == 0xED) { + if (strncmp($jpegHeader[$i]['data'], "Photoshop 3.0\x00", 14) == 0) { + return $jpegHeader[$i]['data']; + } + } + } + return FALSE; +} + + +function getXmpDom($jpegHeader) +{ + for ($i = 0; $i < count($jpegHeader); $i++) { + if ($jpegHeader[$i]['type'] == 0xE1) { + if (strncmp($jpegHeader[$i]['data'], "http://ns.adobe.com/xap/1.0/\x00", 29) == 0) { + $xmlstr = substr($jpegHeader[$i]['data'], 29); + $doc = new DOMDocument(); + $doc->loadXML($xmlstr); + return $doc; + } + } + } + return FALSE; +} + + +function getXmpValue($dom, $xpathQuery) +{ + if ($dom === FALSE) + return null; + $xpath = new DOMXPath($dom); + $xpath->registerNamespace('rdf', "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); + $xpath->registerNamespace('photoshop', "http://ns.adobe.com/photoshop/1.0/"); + $xpath->registerNamespace('Iptc4xmpCore', "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"); + $xpath->registerNamespace('dc', "http://purl.org/dc/elements/1.1/"); + $xpath->registerNamespace('mediapro', "http://ns.iview-multimedia.com/mediapro/1.0/"); + $nodeList = $xpath->query($xpathQuery); + $result = ""; + foreach ($nodeList as $node) { + if (!empty($result)) + $result .= ';'; + $result .= $node->nodeValue; + } + return $result; +} + diff --git a/modules/iptc/models/iptc_key.php b/modules/iptc/models/iptc_key.php new file mode 100644 index 00000000..fadcb37b --- /dev/null +++ b/modules/iptc/models/iptc_key.php @@ -0,0 +1,21 @@ + +
+

+
+ +
+
diff --git a/modules/iptc/views/iptc_block.html.php b/modules/iptc/views/iptc_block.html.php new file mode 100644 index 00000000..c446935a --- /dev/null +++ b/modules/iptc/views/iptc_block.html.php @@ -0,0 +1,9 @@ + + diff --git a/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php b/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php index 8fc45758..9045b4dc 100644 --- a/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php +++ b/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php @@ -212,14 +212,14 @@ class IdentityProvider_Ldap_Driver implements IdentityProvider_Driver { /** * @see IdentityProvider_Driver::add_user_to_group. */ - static function add_user_to_group($user, $group_id) { + static function add_user_to_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } /** * @see IdentityProvider_Driver::remove_user_to_group. */ - static function remove_user_from_group($user, $group_id) { + static function remove_user_from_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } } // End Identity Gallery Driver diff --git a/modules/moduleupdates/controllers/admin_moduleupdates.php b/modules/moduleupdates/controllers/admin_moduleupdates.php index ba812515..1a7a2508 100644 --- a/modules/moduleupdates/controllers/admin_moduleupdates.php +++ b/modules/moduleupdates/controllers/admin_moduleupdates.php @@ -39,40 +39,136 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { * @author brentil */ public function index() { + $view = new Admin_View("admin.html"); $view->page_title = t("Gallery 3 :: Manage Module Updates"); $view->content = new View("admin_moduleupdates.html"); - $all_modules = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); + $devDebug = false; + $refreshCache = false; + + $cache = unserialize(Cache::instance()->get("moduleupdates_cache")); + $cache_updates = unserialize(Cache::instance()->get("moduleupdates_cache_updates")); + + //--------------------------------------------------------------------------------------------- + //echo 'Message 01: ' .$cache_updates . '
'; + //--------------------------------------------------------------------------------------------- + + //if someone pressed the button to refresh now + if (request::method() == "post") { + access::verify_csrf(); + $cache = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); + $cache_updates = array("date" => "", "updates" => 0); + $refreshCache = true; + }else if(count($cache) < 1 or $cache_updates['date'] == ""){ + //if there are no items in the cache array or the update date is "" refresh the data + $cache = new ArrayObject(array(), ArrayObject::ARRAY_AS_PROPS); + $cache_updates = array("date" => "", "updates" => 0); + $refreshCache = true; + } + + //Check the ability to access the Gallery3 GitHub + $GitHub = null; + try { + $GitHub = fopen ("http://github.com", "r"); + if ($GitHub != null) { + $GitHub = 'Online'; + }else{ + $GitHub = 'Offline'; + } + } + catch (Exception $e) { + //echo 'Message: ' .$e->getMessage() . '
'; + } + //Check the ability to access the Google + $Google = null; + try { + $Google = fopen ("http://google.com", "r"); + if ($Google != null) { + $Google = 'Online'; + }else{ + $Google = 'Offline'; + } + } + catch (Exception $e) { + //echo 'Message: ' .$e->getMessage() . '
'; + } - foreach (module::available() as $this_module_name => $module_info) { - - $remote_version = ''; - $remote_server = ''; - - list ($remote_version, $remote_server) = $this->get_remote_module_version($this_module_name); - - $font_color = "black"; - if ($remote_version == "DNE") { - $font_color = "blue"; - } else if ($module_info->version != '' and $module_info->code_version < $module_info->version) { - $font_color = "pink"; - } else if ($module_info->version != '' and $module_info->code_version > $module_info->version) { - $font_color = "orange"; - } else if ($remote_version < $module_info->code_version or ($module_info->version != '' and $remote_version < $module_info->version)) { - $font_color = "green"; - } else if ($remote_version > $module_info->code_version or ($module_info->version != '' and $remote_version > $module_info->version)) { - $font_color = "red"; - } + if($refreshCache == true){ + foreach (module::available() as $this_module_name => $module_info) { + + //example code for setting cache values + //Cache::instance()->set($key, "$log{$msg}", array("task", "log", "import"), 2592000); + //example delete cache + //Cache::instance()->delete("update_l10n_cache:{$task->id}"); + //example for reading cache + //$log = Cache::instance()->get($key); + + $remote_version = ''; + $remote_server = ''; + $update_count = 0; + + list ($remote_version, $remote_server) = $this->get_remote_module_version($this_module_name, $devDebug); + + $font_color = "black"; + //BLUE - DNE: Does Not Exist, this module was not found + if ($remote_version == "DNE") { + $font_color = "blue"; + //PINK - Your installed version is newer than file version + } else if ($module_info->version != '' and $module_info->code_version < $module_info->version) { + $font_color = "pink"; + //ORANGE - Your file version is newer than the installed version + } else if ($module_info->version != '' and $module_info->code_version > $module_info->version) { + $font_color = "orange"; + //GREEN - Your version is newer than the GitHub + } else if ($remote_version < $module_info->code_version or ($module_info->version != '' + and $remote_version < $module_info->version)) { + $font_color = "green"; + //RED - Your version is older than the GitHub + } else if ($remote_version > $module_info->code_version or ($module_info->version != '' + and $remote_version > $module_info->version)) { + $font_color = "red"; + $update_count++; + /* + if($remote_server == "(G3)"){ + $module_info->name = "".$module_info->name.""; + }else if($remote_server == "(G3CC)"){ + $module_info->name = "".$module_info->name.""; + }else if($remote_server == "(brentil)"){ + $module_info->name = "".$module_info->name.""; + } + */ + } + + $module_info->name = "".$module_info->name.""; + + //populate the list fo modules and their data + $cache->$this_module_name = array ("name" => $module_info->name, "locked" => $module_info->locked, + "code_version" => $module_info->code_version, "active" => $module_info->active, + "version" => $module_info->version,"description" => $module_info->description, + "remote_version" => $remote_version, "remote_server" => $remote_server, "font_color" => $font_color); + } - $all_modules->$this_module_name = array ("name" => $module_info->name, "locked" => $module_info->locked, - "code_version" => $module_info->code_version, "active" => $module_info->active, - "version" => $module_info->version,"description" => $module_info->description, - "remote_version" => $remote_version, "remote_server" => $remote_server, "font_color" => $font_color); + //Define right now as YYYY.MM.DD HH:MM with the # of updates that are out of date + $cache_updates = array("date" => date("Y.m.d - H:i"), "updates" => $update_count); + + //--------------------------------------------------------------------------------------------- + //echo 'Message 02: ' .$cache_updates . '
'; + //--------------------------------------------------------------------------------------------- + + //Write out the new data to cache with a 30 day expiration & 0 for update data so it's always present + Cache::instance()->set("moduleupdates_cache", serialize($cache), array("ModuleUpdates"), 30*86400); + Cache::instance()->set("moduleupdates_cache_updates", serialize($cache_updates), array("ModuleUpdates"), null); + log::success("moduleupdates", t("Completed checking remote GitHub for modules updates.")); } + + $view->content->vars = $cache; + $view->content->update_time = $cache_updates['date']; + $view->content->csrf = access::csrf_token(); + $view->content->Google = $Google; + $view->content->GitHub = $GitHub; - $view->content->vars = $all_modules; - + print $view; } @@ -91,22 +187,41 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { * @param String The folder name of the module to search for on the remote GitHub server * @return Array An array with the remote module version and the server it was found on. */ - private function get_remote_module_version ($module_name) { + private function get_remote_module_version ($module_name, $devDebug) { $version = 'DNE'; $server = ''; $file = null; - try { - $file = fopen ("http://github.com/gallery/gallery3/raw/master/modules/".$module_name."/module.info", "r"); - if ($file != null) { - $server = '(G3)'; - } - } - catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
'; + //For development debug only + if ($devDebug == true){ + if ($file == null) { + try { + $file = fopen ("http://github.com/brentil/gallery3-contrib/raw/master/modules/".$module_name."/module.info", "r"); + if ($file != null) { + $server = '(brentil)'; + } + } + catch (Exception $e) { + //echo 'Message: ' .$e->getMessage() . '
'; + } + } } + + //Check the main Gallery3 GitHub + if ($file == null) { + try { + $file = fopen ("http://github.com/gallery/gallery3/raw/master/modules/".$module_name."/module.info", "r"); + if ($file != null) { + $server = '(G3)'; + } + } + catch (Exception $e) { + //echo 'Message: ' .$e->getMessage() . '
'; + } + } + //Check the Gallery3 Community Contributions GitHub if ($file == null) { try { $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/modules/".$module_name."/module.info", "r"); @@ -118,7 +233,7 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { //echo 'Message: ' .$e->getMessage() . '
'; } } - + if ($file != null) { while (!feof ($file)) { $line = fgets ($file, 1024); diff --git a/modules/moduleupdates/helpers/moduleupdates_installer.php b/modules/moduleupdates/helpers/moduleupdates_installer.php index c15f4d82..dd0dddb8 100644 --- a/modules/moduleupdates/helpers/moduleupdates_installer.php +++ b/modules/moduleupdates/helpers/moduleupdates_installer.php @@ -17,21 +17,35 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + class moduleupdates_installer { static function install() { $version = module::get_version("moduleupdates"); if ($version == 0) { - module::set_version("moduleupdates", 1); + module::set_version("moduleupdates", 2); + //Remove the ModuleUpdates cache entry 'JIC' + Cache::instance()->delete("ModuleUpdates"); + //create the blank ModuleUpdates cache entry with an expiration of 0 days + Cache::instance()->set("moduleupdates_cache", "", array("ModuleUpdates"), null); + Cache::instance()->set("moduleupdates_cache_updates", "", array("ModuleUpdates"), null); } } static function upgrade($version) { + module::set_version("moduleupdates", 2); + //Remove the ModuleUpdates cache entry 'JIC' + Cache::instance()->delete("ModuleUpdates"); + //Empty the ModuleUpdates cache entry so our new version starts from scratch + Cache::instance()->set("moduleupdates_cache", "", array("ModuleUpdates"), null); + Cache::instance()->set("moduleupdates_cache_updates", "", array("ModuleUpdates"), null); } static function uninstall() { - + + //Remove the ModuleUpdates cache entry as we remove the module + Cache::instance()->delete("ModuleUpdates"); module::delete("moduleupdates"); } } diff --git a/modules/moduleupdates/module.info b/modules/moduleupdates/module.info index 446d30db..8fad54ff 100644 --- a/modules/moduleupdates/module.info +++ b/modules/moduleupdates/module.info @@ -1,3 +1,3 @@ name = "Module Updates" description = "Compares your installed module version against the ones stored in the GitHub." -version = 1 +version = 2 diff --git a/modules/moduleupdates/views/admin_moduleupdates.html.php b/modules/moduleupdates/views/admin_moduleupdates.html.php index 8a7c6494..3fdc06a7 100644 --- a/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -1,21 +1,33 @@ +
-

-

-
") ?> - Red = Your version is older than the GitHub
") ?> - Green = Your version is newer than the GitHub
") ?> - Orange = Your file version is newer than the installed version
") ?> - Pink = Your installed version is newer than file version
") ?> - Blue = Does Not Exist/No information available
") ?> -

- -
    -
  • -
- +

+ +
+ +
" id="g-configure-moduleupdates-form"> + +
+ ModuleUpdates Information +
    +
  • Red = Your version is older than the GitHub
    ") ?>
  • +
  • Green = Your version is newer than the GitHub
    ") ?>
  • +
  • Orange = Your file version is newer than the installed version
    ") ?>
  • +
  • Pink = Your installed version is newer than file version
    ") ?>
  • +
  • Blue = Does Not Exist/No information available
    ") ?>
  • +
  • ") ?>
  • +
  • " class="submit" />
  • +
+
+
+ +
+
    +
  • +
+ @@ -25,14 +37,12 @@ "> - + - +
"; ?> "; ?> "; ?> "; ?> "; ?> "; ?>
-
-
-
\ No newline at end of file +
\ No newline at end of file diff --git a/modules/photoannotation/controllers/admin_photoannotation.php b/modules/photoannotation/controllers/admin_photoannotation.php new file mode 100644 index 00000000..535c004a --- /dev/null +++ b/modules/photoannotation/controllers/admin_photoannotation.php @@ -0,0 +1,289 @@ +_get_view(); + } + + public function converter() { + print $this->_get_converter_view(); + } + + public function tagsmaintanance($delete) { + print $this->_get_tagsmaintanance_view($delete); + } + + public function converthandler() { + access::verify_csrf(); + $form = $this->_get_converter_form(); + if ($form->validate()) { + //Load the source tag + $sourcetag = ORM::factory("tag", $form->sourcetag->value); + if (!$sourcetag->loaded()) { + message::error(t("The specified tag could not be found")); + url::redirect("admin/photoannotation/converter"); + } + //Load the target user + $targetuser = ORM::factory("user", $form->targetuser->value); + if (!$targetuser->loaded()) { + message::error(t("The specified user could not be found")); + url::redirect("admin/photoannotation/converter"); + } + //Load all existing tag annotations + $tag_annotations = ORM::factory("items_face")->where("tag_id", "=", $sourcetag->id)->find_all(); + + foreach ($tag_annotations as $tag_annotation) { + photoannotation::saveuser($targetuser->id, $tag_annotation->item_id, $tag_annotation->x1, $tag_annotation->y1, $tag_annotation->x2, $tag_annotation->y2, $tag_annotation->description); + //Delete the old annotation + $tag_annotation->delete(); + } + message::success(t("%count tag annotations (%tagname) have been converted to user annotations (%username)", array("count" => count($tag_annotations), "tagname" => $sourcetag->name, "username" => $targetuser->display_name()))); + url::redirect("admin/photoannotation/converter"); + } + print $this->_get_converter_view($form); + } + + public function handler() { + access::verify_csrf(); + + $form = $this->_get_form(); + if ($form->validate()) { + module::set_var( + "photoannotation", "noborder", $form->hoverphoto->noborder->value, true); + module::set_var( + "photoannotation", "bordercolor", $form->hoverphoto->bordercolor->value); + module::set_var( + "photoannotation", "noclickablehover", $form->hoverclickable->noclickablehover->value, true); + module::set_var( + "photoannotation", "clickablehovercolor", $form->hoverclickable->clickablehovercolor->value); + module::set_var( + "photoannotation", "nohover", $form->hovernoclickable->nohover->value, true); + module::set_var( + "photoannotation", "hovercolor", $form->hovernoclickable->hovercolor->value); + module::set_var( + "photoannotation", "showusers", $form->legendsettings->showusers->value, true); + module::set_var( + "photoannotation", "showfaces", $form->legendsettings->showfaces->value, true); + module::set_var( + "photoannotation", "shownotes", $form->legendsettings->shownotes->value, true); + module::set_var( + "photoannotation", "fullname", $form->legendsettings->fullname->value, true); + module::set_var( + "photoannotation", "nonotifications", $form->notifications->nonotifications->value, true); + module::set_var( + "photoannotation", "notificationoptout", $form->notifications->notificationoptout->value, true); + module::set_var( + "photoannotation", "newtagsubject", $form->newtagmail->newtagsubject->value); + module::set_var( + "photoannotation", "newtagbody", $form->newtagmail->newtagbody->value); + module::set_var( + "photoannotation", "newcommentsubject", $form->newcommentmail->newcommentsubject->value); + module::set_var( + "photoannotation", "newcommentbody", $form->newcommentmail->newcommentbody->value); + module::set_var( + "photoannotation", "updatedcommentsubject", $form->updatedcommentmail->updatedcommentsubject->value); + module::set_var( + "photoannotation", "updatedcommentbody", $form->updatedcommentmail->updatedcommentbody->value); + module::set_var( + "photoannotation", "onuserdelete", $form->onuserdelete->onuserdelete->value); + message::success(t("Your settings have been saved.")); + url::redirect("admin/photoannotation"); + } + print $this->_get_view($form); + } + + private function _get_view($form=null) { + $v = new Admin_View("admin.html"); + $v->page_title = t("Photo annotation"); + $v->content = new View("admin_photoannotation.html"); + $v->content->form = empty($form) ? $this->_get_form() : $form; + return $v; + } + + private function _get_converter_view($form=null) { + $v = new Admin_View("admin.html"); + $v->page_title = t("Photo annotation converter"); + $v->content = new View("admin_photoannotation_converter.html"); + $v->content->form = empty($form) ? $this->_get_converter_form() : $form; + return $v; + } + + private function _get_tagsmaintanance_view($delete = false) { + $tag_orpanes_count = 0; + $user_orphanes_count = 0; + $item_orphanes_count = 0; + $tag_orpanes_deleted = 0; + $user_orphanes_deleted = 0; + $item_orphanes_deleted = 0; + //check all tag annotations + $tag_annotations = ORM::factory("items_face")->find_all(); + foreach ($tag_annotations as $tag_annotation) { + $check_tag = ORM::factory("tag")->where("id", "=", $tag_annotation->tag_id)->find(); + if (!$check_tag->loaded()) { + if ($delete) { + $tag_annotation->delete(); + $tag_orpanes_deleted++; + } else { + $tag_orpanes_count++; + } + } else { + $check_item = ORM::factory("item")->where("id", "=", $tag_annotation->item_id)->find(); + if (!$check_item->loaded()) { + if ($delete) { + $tag_annotation->delete(); + $item_orpanes_deleted++; + } else { + $item_orpanes_count++; + } + } + } + } + //check all user annotations + $user_annotations = ORM::factory("items_user")->find_all(); + foreach ($user_annotations as $user_annotation) { + $check_user = ORM::factory("user")->where("id", "=", $user_annotation->user_id)->find(); + if (!$check_user->loaded()) { + if ($delete) { + $user_annotation->delete(); + $user_orpanes_deleted++; + } else { + $user_orphanes_count++; + } + } else { + $check_item = ORM::factory("item")->where("id", "=", $user_annotation->item_id)->find(); + if (!$check_item->loaded()) { + if ($delete) { + $user_annotation->delete(); + $item_orpanes_deleted++; + } else { + $item_orpanes_count++; + } + } + } + } + + //check all user annotations + $note_annotations = ORM::factory("items_note")->find_all(); + foreach ($note_annotations as $note_annotation) { + $check_item = ORM::factory("item")->where("id", "=", $note_annotation->item_id)->find(); + if (!$check_item->loaded()) { + if ($delete) { + $note_annotation->delete(); + $item_orpanes_deleted++; + } else { + $item_orpanes_count++; + } + } + } + $v = new Admin_View("admin.html"); + $v->page_title = t("Photo annotation tags maintanance"); + $v->content = new View("admin_photoannotation_tagsmaintanance.html"); + $v->content->tag_orpanes_count = $tag_orpanes_count; + $v->content->user_orphanes_count = $user_orphanes_count; + $v->content->item_orphanes_count = $item_orphanes_count; + $v->content->tag_orpanes_deleted = $tag_orpanes_deleted; + $v->content->user_orphanes_deleted = $user_orphanes_deleted; + $v->content->item_orphanes_deleted = $item_orphanes_deleted; + $v->content->dodeletion = $delete; + return $v; + } + + private function _get_converter_form() { + //get all tags + $tags = ORM::factory("tag")->order_by("name", "ASC")->find_all(); + foreach ($tags as $tag) { + $tag_array[$tag->id] = $tag->name; + } + //get all users + $users = ORM::factory("user")->order_by("name", "ASC")->find_all(); + foreach ($users as $user) { + $user_array[$user->id] = $user->display_name(); + } + $form = new Forge("admin/photoannotation/converthandler", "", "post", array("id" => "g-admin-form")); + $form->dropdown("sourcetag")->label(t("Select tag")) + ->options($tag_array); + $form->dropdown("targetuser")->label(t("Select user")) + ->options($user_array); + $form->submit("submit")->value(t("Convert")); + return $form; + } + + private function _get_form() { + if (module::is_active("comment")) { + $comment_required = ""; + } else { + $comment_required = " (comment module required)"; + } + $form = new Forge("admin/photoannotation/handler", "", "post", array("id" => "g-admin-form")); + $group = $form->group("hoverphoto")->label(t("Hovering over the photo")); + $group->checkbox("noborder")->label(t("Don't show borders.")) + ->checked(module::get_var("photoannotation", "noborder", false)); + $group->input("bordercolor")->label(t('Border color')) + ->value(module::get_var("photoannotation", "bordercolor", "000000")) + ->rules("valid_alpha_numeric|length[6]"); + $group = $form->group("hoverclickable")->label(t("Hovering over a clickable annotation")); + $group->checkbox("noclickablehover")->label(t("Don't show borders.")) + ->checked(module::get_var("photoannotation", "noclickablehover", false)); + $group->input("clickablehovercolor")->label(t('Border color')) + ->value(module::get_var("photoannotation", "clickablehovercolor", "00AD00")) + ->rules("valid_alpha_numeric|length[6]"); + $group = $form->group("hovernoclickable")->label(t("Hovering over a non-clickable annotation")); + $group->checkbox("nohover")->label(t("Don't show borders.")) + ->checked(module::get_var("photoannotation", "nohover", false)); + $group->input("hovercolor")->label(t('Border color')) + ->value(module::get_var("photoannotation", "hovercolor", "990000")) + ->rules("valid_alpha_numeric|length[6]"); + $group = $form->group("legendsettings")->label(t("Legend settings")); + $group->checkbox("showusers")->label(t("Show face annotation below photo.")) + ->checked(module::get_var("photoannotation", "showusers", false)); + $group->checkbox("showfaces")->label(t("Show face annotation below photo.")) + ->checked(module::get_var("photoannotation", "showfaces", false)); + $group->checkbox("shownotes")->label(t("Show note annotations below photo.")) + ->checked(module::get_var("photoannotation", "shownotes", false)); + $group->checkbox("fullname")->label(t("Show full name of a user instead of the username on annotations (username will be dispayed for users without a full name).")) + ->checked(module::get_var("photoannotation", "fullname", false)); + $group = $form->group("notifications")->label(t("Notification settings")); + $group->checkbox("nonotifications")->label(t("Disable user notifications.")) + ->checked(module::get_var("photoannotation", "nonotifications", false)); + $group->checkbox("notificationoptout")->label(t("Notify users by default (only applies to new users and user who have not saved their profile after installing this module).")) + ->checked(module::get_var("photoannotation", "notificationoptout", false)); + $group = $form->group("newtagmail")->label(t("Customize the mail sent to users when a user annotation is created")); + $group->input("newtagsubject")->label(t("Subject")) + ->value(module::get_var("photoannotation", "newtagsubject", "Someone tagged a photo of you")); + $group->textarea("newtagbody")->label(t("Body (allowed placeholders: %name = name of the recipient, %url = link to the item that was tagged)")) + ->value(module::get_var("photoannotation", "newtagbody", "Hello %name, please visit %url to view the photo.")); + $group = $form->group("newcommentmail")->label(t("Customize the mail sent to users when a comment is added". $comment_required)); + $group->input("newcommentsubject")->label(t("Subject")) + ->value(module::get_var("photoannotation", "newcommentsubject", "Someone added a comment to photo of you")); + $group->textarea("newcommentbody")->label(t("Body (allowed placeholders: %name = name of the recipient, %url = link to the item that was commented on)")) + ->value(module::get_var("photoannotation", "newcommentbody", "Hello %name, please visit %url to read the comment.")); + $group = $form->group("updatedcommentmail")->label(t("Customize the mail sent to users when a comment is updated". $comment_required)); + $group->input("updatedcommentsubject")->label(t("Subject")) + ->value(module::get_var("photoannotation", "updatedcommentsubject", "Someone updated a comment to photo of you")); + $group->textarea("updatedcommentbody")->label(t("Body (allowed placeholders: %name = name of the recipient, %url = link to the item that was commented on)")) + ->value(module::get_var("photoannotation", "updatedcommentbody", "Hello %name, please visit %url to read the comment.")); + $group = $form->group("onuserdelete")->label(t("Auto conversion settings")); + $group->dropdown("onuserdelete")->label(t("When deleting a user do the following with all annotations associated with this user")) + ->options(array("0" => t("Delete annotation"), "1" => t("Convert to tag annotation"), "2" => t("Convert to note annotation"))) + ->selected(module::get_var("photoannotation", "onuserdelete", "0")); + $form->submit("submit")->value(t("Save")); + return $form; + } +} diff --git a/modules/photoannotation/controllers/photoannotation.php b/modules/photoannotation/controllers/photoannotation.php new file mode 100644 index 00000000..0ba8a79c --- /dev/null +++ b/modules/photoannotation/controllers/photoannotation.php @@ -0,0 +1,164 @@ +type}s/{$item->id}"); + //Add tag to item, create tag if not exists + if ($tag_data != "") { + $tag = ORM::factory("tag")->where("name", "=", $tag_data)->find(); + if (!$tag->loaded()) { + $tag->name = $tag_data; + $tag->count = 0; + } + $tag->add($item); + $tag->count++; + $tag->save(); + $tag_data = $tag->id; + } else { + $tag_data = -1; + } + //Save annotation + if ($annotate_id == "new") { //This is a new annotation + if ($user_id > -1) { //Save user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } elseif ($tag_data > -1) { //Conversion user -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } elseif ($item_title != "") { //Conversion user -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } else { //Somethings wrong + message::error(t("Please select a User or Tag or specify a Title.")); + url::redirect($redir_uri); + return; + } + } else { //This is an update to an existing annotation + switch ($notetype) { + case "user": //the original annotation is a user + $updateduser = ORM::factory("items_user") //load the existing user + ->where("id", "=", $annotate_id) + ->find(); + if ($user_id > -1) { //Conversion user -> user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + } elseif ($tag_data > -1) { //Conversion user -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updateduser->delete(); //delete old user + } elseif ($item_title != "") { //Conversion user -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updateduser->delete(); //delete old user + } else { //Somethings wrong + message::error(t("Please select a User or Tag or specify a Title.")); + url::redirect($redir_uri); + return; + } + break; + case "face": //the original annotation is a face + $updatedface = ORM::factory("items_face") //load the existing user + ->where("id", "=", $annotate_id) + ->find(); + if ($user_id > -1) { //Conversion face -> user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatedface->delete(); //delete old face + } elseif ($tag_data > -1) { //Conversion face -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id); + } elseif ($item_title != "") { //Conversion face -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatedface->delete(); //delete old face + } else { //Somethings wrong + message::error(t("Please select a User or Tag or specify a Title.")); + url::redirect($redir_uri); + return; + } + break; + case "note": //the original annotation is a note + $updatednote = ORM::factory("items_note") //load the existing user + ->where("id", "=", $annotate_id) + ->find(); + if ($user_id > -1) { //Conversion note -> user + photoannotation::saveuser($user_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatednote->delete(); //delete old note + } elseif ($tag_data > -1) { //Conversion note -> face + photoannotation::saveface($tag_data, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description); + $updatednote->delete(); //delete old note + } elseif ($item_title != "") { //Conversion note -> note + photoannotation::savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id); + } else { //Somethings wrong + message::error(t("Please select a User or Tag or specify a Title.")); + url::redirect($redir_uri); + return; + } + break; + default: + message::error(t("Please select a User or Tag or specify a Title.")); + url::redirect($redir_uri); + return; + } + } + message::success(t("Annotation saved.")); + url::redirect($redir_uri); + return; + } + + public function delete($item_data) { + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + //Get form data + $item = ORM::factory("item", $item_data); + $noteid = $_POST["noteid"]; + $notetype = $_POST["notetype"]; + $redir_uri = url::abs_site("{$item->type}s/{$item->id}"); + + if ($noteid == "") { + message::error(t("Please select a tag or note to delete.")); + url::redirect($redir_uri); + return; + } + switch ($notetype) { + case "user": + db::build()->delete("items_users")->where("id", "=", $noteid)->execute(); + break; + case "face": + db::build()->delete("items_faces")->where("id", "=", $noteid)->execute(); + break; + case "note": + db::build()->delete("items_notes")->where("id", "=", $noteid)->execute(); + break; + default: + message::error(t("Please select a tag or note to delete.")); + url::redirect($redir_uri); + return; + } + message::success(t("Annotation deleted.")); + url::redirect($redir_uri); + } +} diff --git a/modules/photoannotation/css/colorpicker.css b/modules/photoannotation/css/colorpicker.css new file mode 100644 index 00000000..05b02b48 --- /dev/null +++ b/modules/photoannotation/css/colorpicker.css @@ -0,0 +1,161 @@ +.colorpicker { + width: 356px; + height: 176px; + overflow: hidden; + position: absolute; + background: url(../images/colorpicker_background.png); + font-family: Arial, Helvetica, sans-serif; + display: none; +} +.colorpicker_color { + width: 150px; + height: 150px; + left: 14px; + top: 13px; + position: absolute; + background: #f00; + overflow: hidden; + cursor: crosshair; +} +.colorpicker_color div { + position: absolute; + top: 0; + left: 0; + width: 150px; + height: 150px; + background: url(../images/colorpicker_overlay.png); +} +.colorpicker_color div div { + position: absolute; + top: 0; + left: 0; + width: 11px; + height: 11px; + overflow: hidden; + background: url(../images/colorpicker_select.gif); + margin: -5px 0 0 -5px; +} +.colorpicker_hue { + position: absolute; + top: 13px; + left: 171px; + width: 35px; + height: 150px; + cursor: n-resize; +} +.colorpicker_hue div { + position: absolute; + width: 35px; + height: 9px; + overflow: hidden; + background: url(../images/colorpicker_indic.gif) left top; + margin: -4px 0 0 0; + left: 0px; +} +.colorpicker_new_color { + position: absolute; + width: 60px; + height: 30px; + left: 213px; + top: 13px; + background: #f00; +} +.colorpicker_current_color { + position: absolute; + width: 60px; + height: 30px; + left: 283px; + top: 13px; + background: #f00; +} +.colorpicker input { + background-color: transparent; + border: 1px solid transparent; + position: absolute; + font-size: 10px; + font-family: Arial, Helvetica, sans-serif; + color: #898989; + top: 4px; + right: 11px; + text-align: right; + margin: 0; + padding: 0; + height: 11px; +} +.colorpicker_hex { + position: absolute; + width: 72px; + height: 22px; + background: url(../images/colorpicker_hex.png) top; + left: 212px; + top: 142px; +} +.colorpicker_hex input { + right: 6px; +} +.colorpicker_field { + height: 22px; + width: 62px; + background-position: top; + position: absolute; +} +.colorpicker_field span { + position: absolute; + width: 12px; + height: 22px; + overflow: hidden; + top: 0; + right: 0; + cursor: n-resize; +} +.colorpicker_rgb_r { + background-image: url(../images/colorpicker_rgb_r.png); + top: 52px; + left: 212px; +} +.colorpicker_rgb_g { + background-image: url(../images/colorpicker_rgb_g.png); + top: 82px; + left: 212px; +} +.colorpicker_rgb_b { + background-image: url(../images/colorpicker_rgb_b.png); + top: 112px; + left: 212px; +} +.colorpicker_hsb_h { + background-image: url(../images/colorpicker_hsb_h.png); + top: 52px; + left: 282px; +} +.colorpicker_hsb_s { + background-image: url(../images/colorpicker_hsb_s.png); + top: 82px; + left: 282px; +} +.colorpicker_hsb_b { + background-image: url(../images/colorpicker_hsb_b.png); + top: 112px; + left: 282px; +} +.colorpicker_submit { + position: absolute; + width: 22px; + height: 22px; + background: url(../images/colorpicker_submit.png) top; + left: 322px; + top: 142px; + overflow: hidden; +} +.colorpicker_focus { + background-position: center; +} +.colorpicker_hex.colorpicker_focus { + background-position: bottom; +} +.colorpicker_submit.colorpicker_focus { + background-position: bottom; +} +.colorpicker_slider { + background-position: bottom; +} diff --git a/modules/photoannotation/css/photoannotation.css b/modules/photoannotation/css/photoannotation.css new file mode 100644 index 00000000..de3e6f87 --- /dev/null +++ b/modules/photoannotation/css/photoannotation.css @@ -0,0 +1,195 @@ +.image-annotate-canvas { + background-position: left top; + background-repeat: no-repeat; + display: block; + margin: 0 auto; + position: relative; +} +.image-annotate-view { + display: none; + position: relative; +} +.image-annotate-area { + position: absolute; + cursor: default; +} +.image-annotate-area div { + display: block; +} +.image-annotate-area-editable { + cursor: pointer; +} +.image-annotate-note { + background: #000000 none repeat scroll 0 0; + color: #FFFFFF; + display: none; + font-family: Verdana, Sans-Serif; + font-size: 1.4em; + max-width: 200px; + padding: 3px 7px; + position: absolute; +} +.image-annotate-note .actions { + display: block; + font-size: 80%; +} +.image-annotate-edit { + display: none; + direction: ltr !important; +} +#image-annotate-edit-form { + background: #FFFFFF none repeat scroll 0 0; + border: 1px solid #000000; + padding: 7px; + position: absolute; + width: 250px; +} +.image-annotate-rtl form { + text-align: right !important; +} +#image-annotate-edit-form form { + clear: right; + margin: 0 !important; + padding: 0; + z-index: 999; + text-align: left; + color: #000000; +} +#image-annotate-edit-form .box { + margin: 0; +} +#image-annotate-edit-form input.form-text, #image-annotate-edit-form #edit-comment-wrapper textarea { + width: 90%; +} +#image-annotate-edit-form textarea { + height: 50px; + font-family: Verdana, Sans-Serif; + font-size: 12px; + width: 248px; + resize: none; +} +#image-annotate-edit-form fieldset { + background: transparent none repeat scroll 0 0; +} +#image-annotate-edit-form .form-item { + margin: 0 0 5px; +} +#image-annotate-edit-form .form-button, #image-annotate-edit-form .form-submit { + margin: 0; +} +#image-annotate-edit-form a { + cursor: pointer; + display: block; + float: left; + margin: 3px 6px 3px 0; +} +.image-annotate-rtl a { + float: right !important; + margin: 3px 0 3px 6px !important; +} +.inmage-annotate-dialog button{ + float: left !important; +} +.inmage-annotate-dialog-rtl button{ + float: left !important; +} +.image-annotate-edit-area { + border: 1px solid black; + cursor: move; + display: block; + height: 60px; + left: 10px; + margin: 0; + padding: 0; + position: absolute; + top: 10px; + width: 60px; +} +.image-annotate-edit-area .ui-resizable-handle { + opacity: 0.8; +} +.image-annotate-edit-ok { + /*background-image: url(../images/accept.png);*/ +} +.image-annotate-edit-delete { + background-image: url(../images/delete.png); +} +.image-annotate-edit-close { + /*background-image: url(../images/cross.png);*/ +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + z-index: 99999; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable- autohide .ui-resizable-handle { + display: block; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0px; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0px; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0px; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0px; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.photoannotation-del-button { + background-image: url('../images/delete.png'); + cursor: pointer; +} +.photoannotation-edit-button { + background-image: url('../images/edit.png'); + cursor: pointer; +} diff --git a/modules/photoannotation/helpers/photoannotation.php b/modules/photoannotation/helpers/photoannotation.php new file mode 100644 index 00000000..3e45265f --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation.php @@ -0,0 +1,171 @@ +where("user_id", "=", $user_id) + ->where("item_id", "=", $item_id) + ->find_all(); + if (count($item_old_users) > 1) { + foreach ($item_old_users as $item_old_user) { + $item_old_user->delete(); + } + $item_user = ORM::factory("items_user"); + } elseif (count($item_old_users) == 1) { + $item_user = ORM::factory("items_user", $item_old_users[0]->id); + } else { + $item_user = ORM::factory("items_user"); + photoannotation::send_notifications($user_id, $item_id, "newtag"); + } + $item_user->user_id = $user_id; + $item_user->item_id = $item_id; + $item_user->x1 = $str_x1; + $item_user->y1 = $str_y1; + $item_user->x2 = $str_x2; + $item_user->y2 = $str_y2; + $item_user->description = $description; + $item_user->save(); + } + + public static function saveface($tag_id, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id = "") { + if ($annotate_id == "") { + $item_face = ORM::factory("items_face"); + } else { + $item_face = ORM::factory("items_face") + ->where("id", "=", $annotate_id) + ->find(); + } + $item_face->tag_id = $tag_id; + $item_face->item_id = $item_id; + $item_face->x1 = $str_x1; + $item_face->y1 = $str_y1; + $item_face->x2 = $str_x2; + $item_face->y2 = $str_y2; + $item_face->description = $description; + $item_face->save(); + } + + public static function savenote($item_title, $item_id, $str_x1, $str_y1, $str_x2, $str_y2, $description, $annotate_id = "") { + if ($annotate_id == "") { + $item_note = ORM::factory("items_note"); + } else { + $item_note = ORM::factory("items_note") + ->where("id", "=", $annotate_id) + ->find(); + } + $item_note->item_id = $item_id; + $item_note->x1 = $str_x1; + $item_note->y1 = $str_y1; + $item_note->x2 = $str_x2; + $item_note->y2 = $str_y2; + $item_note->title = $item_title; + $item_note->description = $description; + $item_note->save(); + } + + public static function send_notifications($recipient_id, $item_id, $mailtype) { + //Load the item + $item = ORM::factory("item")->where("id", "=", $item_id)->find(); + if (!$item->loaded()) { + return false; + } + //Load the user + $recipient = ORM::factory("user")->where("id", "=", $recipient_id)->find(); + if (!$recipient->loaded()) { + return false; + } + //Only send mail if the notifications are switched on globally + if (!module::get_var("photoannotation", "nonotifications", false)) { + //Get the users settings + $notification_settings = self::get_user_notification_settings($recipient); + //Check which type of mail to send + switch ($mailtype) { + case "newtag": + //Only send if user has this option enabled + if ($notification_settings->newtag) { + //Get subject and body and send the mail + $subject = module::get_var("photoannotation", "newtagsubject", "Someone tagged a photo of you"); + $body = module::get_var("photoannotation", "newtagbody", "Hello %name, please visit %url to view the photo."); + $body = str_ireplace(array("%url", "%name"), array($item->abs_url(), $recipient->display_name()), $body); + return self::_send_mail($recipient->email, $subject, $body); + } + break; + case "newcomment": + //Only send if user has this option enabled + if ($notification_settings->comment) { + //Get subject and body and send the mail + $subject = module::get_var("photoannotation", "newcommentsubject", "Someone added a comment to photo of you"); + $body = module::get_var("photoannotation", "newcommentbody", "Hello %name, please visit %url to read the comment."); + $body = str_ireplace(array("%url", "%name"), array($item->abs_url(), $recipient->display_name()), $body); + return self::_send_mail($recipient->email, $subject, $body); + } + break; + case "updatecomment": + //Only send if user has this option enabled + if ($notification_settings->comment) { + //Get subject and body and send the mail + $subject = module::get_var("photoannotation", "updatedcommentsubject", "Someone updated a comment to photo of you"); + $body = module::get_var("photoannotation", "updatedcommentbody", "Hello %name, please visit %url to read the comment."); + $body = str_ireplace(array("%url", "%name"), array($item->abs_url(), $recipient->display_name()), $body); + return self::_send_mail($recipient->email, $subject, $body); + } + } + } + return false; + } + + private static function _send_mail($mailto, $subject, $message) { + //Send the notification mail + return Sendmail::factory() + ->to($mailto) + ->subject($subject) + ->header("Mime-Version", "1.0") + ->header("Content-type", "text/html; charset=utf-8") + ->message($message) + ->send(); + } + + public static function get_user_notification_settings($user) { + //Try loading the notification settings of user + $notification_settings = ORM::factory("photoannotation_notification")->where("user_id", "=", $user->id)->find(); + if (!$notification_settings->loaded()) { + //If the user did not save his settings use the website default + $notify = module::get_var("photoannotation", "notificationoptout", false); + $notification_settings = ORM::factory("photoannotation_notification"); + $notification_settings->user_id = $user->id; + $notification_settings->newtag = $notify; + $notification_settings->comment = $notify; + $notification_settings->save(); + } + return $notification_settings; + } + + public static function get_user_cloud() { + $users = ORM::factory("user")->order_by("name", "ASC")->find_all(); + foreach ($users as $user) { + $items_users_count = ORM::factory("items_user")->where("user_id", "=", $user->id)->count_all(); + if ($items_users_count > 0) { + $user_array[] = $user->display_name(); + } + } + return $user_array; + } +} \ No newline at end of file diff --git a/modules/photoannotation/helpers/photoannotation_event.php b/modules/photoannotation/helpers/photoannotation_event.php new file mode 100644 index 00000000..cf841ac3 --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_event.php @@ -0,0 +1,240 @@ +deactivate)) { + site_status::warning( + t("The Photo Annotation module requires the Tags module. " . + "Activate the Tags module now", + array("url" => url::site("admin/modules"))), + "photoannotation_needs_tag"); + } else { + site_status::clear("photoannotation_needs_tag"); + } + if (module::is_active("tagfaces") || in_array("tagfaces", $changes->activate)) { + site_status::warning( + t("The Photo Annotation module cannot be used together with the TagFaces module. " . + "Dectivate the TagFaces module now", + array("url" => url::site("admin/modules"))), + "photoannotation_incompatibility_tagfaces"); + } else { + site_status::clear("photoannotation_incompatibility_tagfaces"); + } + } + + static function site_menu($menu, $theme) { + // Create a menu option for adding face data. + if (!$theme->item()) { + return; + } + $item = $theme->item(); + if ($item->is_photo()) { + if ((access::can("view", $item)) && (access::can("edit", $item))) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("photoannotation") + ->label(t("Add annotation")) + ->css_id("g-photoannotation-link") + ->url("#")); + } + } + } + + static function item_deleted($item) { + // Check for and delete existing Faces and Notes. + $existingFaces = ORM::factory("items_face") + ->where("item_id", "=", $item->id) + ->find_all(); + if (count($existingFaces) > 0) { + db::build()->delete("items_faces")->where("item_id", "=", $item->id)->execute(); + } + + $existingNotes = ORM::factory("items_note") + ->where("item_id", "=", $item->id) + ->find_all(); + if (count($existingNotes) > 0) { + db::build()->delete("items_notes")->where("item_id", "=", $item->id)->execute(); + } + + $existingUsers = ORM::factory("items_user") + ->where("item_id", "=", $item->id) + ->find_all(); + if (count($existingUsers) > 0) { + db::build()->delete("items_users")->where("item_id", "=", $item->id)->execute(); + } + } + + static function user_deleted($old) { + // Check for and delete existing Annotations linked to that user. + $existingFaces = ORM::factory("items_user") + ->where("user_id", "=", $old->id) + ->find_all(); + if (count($existingFaces) > 0) { + $onuserdelete = module::get_var("photoannotation", "onuserdelete", "0"); + if (module::get_var("photoannotation", "fullname", false)) { + $new_tag_name = $old->display_name(); + } else { + $new_tag_name = $old->name; + } + switch ($onuserdelete) { + case "1": + //convert to tag + $tag = ORM::factory("tag")->where("name", "=", $new_tag_name)->find(); + if (!$tag->loaded()) { + $tag->name = $new_tag_name; + $tag->count = 0; + } + foreach ($existingFaces as $existingFace) { + $item = ORM::factory("item")->where("id", "=", $existingFace->item_id)->find(); + $tag->add($item); + $tag->count++; + $tag->save(); + photoannotation::saveface($tag->id, $existingFace->item_id, $existingFace->x1, $existingFace->y1, $existingFace->x2, $existingFace->y2, $existingFace->description); + } + break; + case "2": + //convert to note + foreach ($existingFaces as $existingFace) { + photoannotation::savenote($new_tag_name, $existingFace->item_id, $existingFace->x1, $existingFace->y1, $existingFace->x2, $existingFace->y2, $existingFace->description); + } + } + db::build()->delete("items_users")->where("user_id", "=", $old->id)->execute(); + } + // Delete notification settings + $notification_settings = ORM::factory("photoannotation_notification") + ->where("user_id", "=", $old->id) + ->find(); + if ($notification_settings->loaded()) { + $notification_settings->delete(); + } + } + + static function user_created($user) { + // Write notification settings + $notify = module::get_var("photoannotation", "notificationoptout", false); + $notification_settings = ORM::factory("photoannotation_notification"); + $notification_settings->user_id = $user->id; + $notification_settings->newtag = $notify; + $notification_settings->comment = $notify; + $notification_settings->save(); + } + + static function admin_menu($menu, $theme) { + $menu->get("settings_menu") + ->append(Menu::factory("link") + ->id("photoannotation_menu") + ->label(t("Photo Annotation")) + ->url(url::site("admin/photoannotation"))); + } + + static function show_user_profile($data) { + $view = new Theme_View("dynamic.html", "collection", "userprofiles"); + //load thumbs + $item_users = ORM::factory("items_user")->where("user_id", "=", $data->user->id)->find_all(); + $children_count = count($item_users); + foreach ($item_users as $item_user) { + $item_thumb = ORM::factory("item") + ->viewable() + ->where("type", "!=", "album") + ->where("id", "=", $item_user->item_id) + ->find(); + $item_thumbs[] = $item_thumb; + } + $page_size = module::get_var("gallery", "page_size", 9); + $page = (int) Input::instance()->get("page", "1"); + $offset = ($page-1) * $page_size; + $max_pages = max(ceil($children_count / $page_size), 1); + + // Make sure that the page references a valid offset + if ($page < 1) { + url::redirect($album->abs_url()); + } else if ($page > $max_pages) { + url::redirect($album->abs_url("page=$max_pages")); + } + if ($page < $max_pages) { + $next_page_url = url::site("user_profile/show/". $data->user->id ."?page=". ($page + 1)); + $view->set_global("next_page_url", $next_page_url); + $view->set_global("first_page_url", url::site("user_profile/show/". $data->user->id ."?page=". $max_pages)); + } + + if ($page > 1) { + $view->set_global("previous_page_url", url::site("user_profile/show/". $data->user->id ."?page=". ($page - 1))); + $view->set_global("first_page_url", url::site("user_profile/show/". $data->user->id ."?page=1")); + } + $view->set_global("page", $page); + $view->set_global("max_pages", $max_pages); + $view->set_global("page_size", $page_size); + $view->set_global("children", array_slice($item_thumbs, $offset, $page_size));; + $view->set_global("children_count", $children_count); + $view->set_global("total", $max_pages); + $view->set_global("position", t("Page") ." ". $page); + if ($children_count > 0) { + $data->content[] = (object)array("title" => t("Photos"), "view" => $view); + } + } + + static function user_edit_form($user, $form) { + // Allow users to modify notification settings. + if (!module::get_var("photoannotation", "nonotifications", false)) { + $notification_settings = photoannotation::get_user_notification_settings($user); + $user_notification = $form->edit_user->group("edit_notification")->label("Tag notifications"); + $user_notification->checkbox("photoannotation_newtag")->label(t("Notify me when a tag is added to a photo with me")) + ->checked($notification_settings->newtag); + $user_notification->checkbox("photoannotation_comment")->label(t("Notify me if someone comments on a photo with me on it")) + ->checked($notification_settings->comment); + } + } + + static function user_edit_form_completed($user, $form) { + // Save notification settings. + if (!module::get_var("photoannotation", "nonotifications", false)) { + $notification_settings = ORM::factory("photoannotation_notification")->where("user_id", "=", $user->id)->find(); + if (!$notification_settings->loaded()) { + $notification_settings = ORM::factory("photoannotation_notification"); + $notification_settings->user_id = $user->id; + } + $notification_settings->newtag = $form->edit_user->edit_notification->photoannotation_newtag->value; + $notification_settings->comment = $form->edit_user->edit_notification->photoannotation_comment->value; + $notification_settings->save(); + } + } + + static function comment_created($comment) { + //Check if there are any user annotations on the photo and send notification if applicable + $item_users = ORM::factory("items_user")->where("item_id", "=", $comment->item_id)->find_all(); + if (count($item_users) > 0) { + foreach ($item_users as $item_user) { + photoannotation::send_notifications($item_user->user_id, $comment->item_id, "newcomment"); + } + } + } + + static function comment_updated($comment) { + //Check if there are any user annotations on the photo and send notification if applicable + $item_users = ORM::factory("items_user")->where("item_id", "=", $comment->item_id)->find_all(); + if (count($item_users) > 0) { + foreach ($item_users as $item_user) { + photoannotation::send_notifications($item_user->user_id, $comment->item_id, "updatecomment"); + } + } + } +} diff --git a/modules/photoannotation/helpers/photoannotation_installer.php b/modules/photoannotation/helpers/photoannotation_installer.php new file mode 100644 index 00000000..8ea36ecf --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_installer.php @@ -0,0 +1,119 @@ +query("CREATE TABLE IF NOT EXISTS {items_faces} ( + `id` int(9) NOT NULL auto_increment, + `tag_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS {items_notes} ( + `id` int(9) NOT NULL auto_increment, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `title` varchar(64) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS {items_users} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + $db->query("CREATE TABLE IF NOT EXISTS {photoannotation_notifications} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL unique, + `newtag` int(2) default NULL, + `comment` int(2) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + // Set the module's version number. + module::set_version("photoannotation", 3); + } + + static function upgrade($version) { + if ($version == 1) { + module::set_version("photoannotation", $version = 2); + } + if ($version == 2) { + $db = Database::instance(); + $db->query("CREATE TABLE IF NOT EXISTS {items_users} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `x1` int(9) NOT NULL, + `y1` int(9) NOT NULL, + `x2` int(9) NOT NULL, + `y2` int(9) NOT NULL, + `description` varchar(2048) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("photoannotation", $version = 3); + } + if ($version == 3) { + $db = Database::instance(); + $db->query("CREATE TABLE IF NOT EXISTS {photoannotation_notifications} ( + `id` int(9) NOT NULL auto_increment, + `user_id` int(9) NOT NULL unique, + `newtag` int(2) default NULL, + `comment` int(2) default NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("photoannotation", $version = 4); + } + } + + static function deactivate() { + // Clear the require tags message when photoannotation is deactivated. + site_status::clear("photoannotation_needs_tag"); + site_status::clear("photoannotation_incompatibility_tagfaces"); + } + + static function uninstall() { + // Delete the face table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {items_faces};"); + $db->query("DROP TABLE IF EXISTS {items_notes};"); + $db->query("DROP TABLE IF EXISTS {items_users};"); + $db->query("DROP TABLE IF EXISTS {photoannotation_notifications};"); + module::delete("photoannotation"); + } +} diff --git a/modules/photoannotation/helpers/photoannotation_theme.php b/modules/photoannotation/helpers/photoannotation_theme.php new file mode 100644 index 00000000..dcc5daa3 --- /dev/null +++ b/modules/photoannotation/helpers/photoannotation_theme.php @@ -0,0 +1,81 @@ +page_subtype == "photo") { + $theme->css("photoannotation.css"); + $theme->script("jquery.annotate.js"); + $noborder = module::get_var("photoannotation", "noborder", false); + $noclickablehover = module::get_var("photoannotation", "noclickablehover", false); + $nohover = module::get_var("photoannotation", "nohover", false); + $bordercolor = "#". module::get_var("photoannotation", "bordercolor", "000000"); + $v = "\n"; + return $v; + } + } + + static function resize_bottom($theme) { + if ($theme->page_subtype == "photo") { + return new View("photoannotation_highlight_block.html"); + } + } + + static function admin_head($theme) { + if (strpos($theme->content->kohana_filename, "admin_photoannotation.html.php")) { + $theme->css("colorpicker.css"); + $theme->script("colorpicker.js"); + } + } + +} diff --git a/modules/photoannotation/images/blank.gif b/modules/photoannotation/images/blank.gif new file mode 100644 index 00000000..75b945d2 Binary files /dev/null and b/modules/photoannotation/images/blank.gif differ diff --git a/modules/photoannotation/images/colorpicker_background.png b/modules/photoannotation/images/colorpicker_background.png new file mode 100644 index 00000000..8401572f Binary files /dev/null and b/modules/photoannotation/images/colorpicker_background.png differ diff --git a/modules/photoannotation/images/colorpicker_hex.png b/modules/photoannotation/images/colorpicker_hex.png new file mode 100644 index 00000000..4e532d7c Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hex.png differ diff --git a/modules/photoannotation/images/colorpicker_hsb_b.png b/modules/photoannotation/images/colorpicker_hsb_b.png new file mode 100644 index 00000000..dfac595d Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hsb_b.png differ diff --git a/modules/photoannotation/images/colorpicker_hsb_h.png b/modules/photoannotation/images/colorpicker_hsb_h.png new file mode 100644 index 00000000..3977ed9f Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hsb_h.png differ diff --git a/modules/photoannotation/images/colorpicker_hsb_s.png b/modules/photoannotation/images/colorpicker_hsb_s.png new file mode 100644 index 00000000..a2a69973 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_hsb_s.png differ diff --git a/modules/photoannotation/images/colorpicker_indic.gif b/modules/photoannotation/images/colorpicker_indic.gif new file mode 100644 index 00000000..f9fa95e2 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_indic.gif differ diff --git a/modules/photoannotation/images/colorpicker_overlay.png b/modules/photoannotation/images/colorpicker_overlay.png new file mode 100644 index 00000000..561cdd9c Binary files /dev/null and b/modules/photoannotation/images/colorpicker_overlay.png differ diff --git a/modules/photoannotation/images/colorpicker_rgb_b.png b/modules/photoannotation/images/colorpicker_rgb_b.png new file mode 100644 index 00000000..dfac595d Binary files /dev/null and b/modules/photoannotation/images/colorpicker_rgb_b.png differ diff --git a/modules/photoannotation/images/colorpicker_rgb_g.png b/modules/photoannotation/images/colorpicker_rgb_g.png new file mode 100644 index 00000000..72b32760 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_rgb_g.png differ diff --git a/modules/photoannotation/images/colorpicker_rgb_r.png b/modules/photoannotation/images/colorpicker_rgb_r.png new file mode 100644 index 00000000..4855fe03 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_rgb_r.png differ diff --git a/modules/photoannotation/images/colorpicker_select.gif b/modules/photoannotation/images/colorpicker_select.gif new file mode 100644 index 00000000..599f7f13 Binary files /dev/null and b/modules/photoannotation/images/colorpicker_select.gif differ diff --git a/modules/photoannotation/images/colorpicker_submit.png b/modules/photoannotation/images/colorpicker_submit.png new file mode 100644 index 00000000..566fe2dd Binary files /dev/null and b/modules/photoannotation/images/colorpicker_submit.png differ diff --git a/modules/photoannotation/images/custom_background.png b/modules/photoannotation/images/custom_background.png new file mode 100644 index 00000000..cf55ffdd Binary files /dev/null and b/modules/photoannotation/images/custom_background.png differ diff --git a/modules/photoannotation/images/custom_hex.png b/modules/photoannotation/images/custom_hex.png new file mode 100644 index 00000000..888f4444 Binary files /dev/null and b/modules/photoannotation/images/custom_hex.png differ diff --git a/modules/photoannotation/images/custom_hsb_b.png b/modules/photoannotation/images/custom_hsb_b.png new file mode 100644 index 00000000..2f99dae8 Binary files /dev/null and b/modules/photoannotation/images/custom_hsb_b.png differ diff --git a/modules/photoannotation/images/custom_hsb_h.png b/modules/photoannotation/images/custom_hsb_h.png new file mode 100644 index 00000000..a217e921 Binary files /dev/null and b/modules/photoannotation/images/custom_hsb_h.png differ diff --git a/modules/photoannotation/images/custom_hsb_s.png b/modules/photoannotation/images/custom_hsb_s.png new file mode 100644 index 00000000..7826b415 Binary files /dev/null and b/modules/photoannotation/images/custom_hsb_s.png differ diff --git a/modules/photoannotation/images/custom_indic.gif b/modules/photoannotation/images/custom_indic.gif new file mode 100644 index 00000000..222fb94c Binary files /dev/null and b/modules/photoannotation/images/custom_indic.gif differ diff --git a/modules/photoannotation/images/custom_rgb_b.png b/modules/photoannotation/images/custom_rgb_b.png new file mode 100644 index 00000000..80764e5d Binary files /dev/null and b/modules/photoannotation/images/custom_rgb_b.png differ diff --git a/modules/photoannotation/images/custom_rgb_g.png b/modules/photoannotation/images/custom_rgb_g.png new file mode 100644 index 00000000..fc9778be Binary files /dev/null and b/modules/photoannotation/images/custom_rgb_g.png differ diff --git a/modules/photoannotation/images/custom_rgb_r.png b/modules/photoannotation/images/custom_rgb_r.png new file mode 100644 index 00000000..91b0cd4c Binary files /dev/null and b/modules/photoannotation/images/custom_rgb_r.png differ diff --git a/modules/photoannotation/images/custom_submit.png b/modules/photoannotation/images/custom_submit.png new file mode 100644 index 00000000..cd202cd9 Binary files /dev/null and b/modules/photoannotation/images/custom_submit.png differ diff --git a/modules/photoannotation/images/delete.png b/modules/photoannotation/images/delete.png new file mode 100644 index 00000000..13eccb1c Binary files /dev/null and b/modules/photoannotation/images/delete.png differ diff --git a/modules/photoannotation/images/edit.png b/modules/photoannotation/images/edit.png new file mode 100644 index 00000000..6dbcb679 Binary files /dev/null and b/modules/photoannotation/images/edit.png differ diff --git a/modules/photoannotation/images/select.png b/modules/photoannotation/images/select.png new file mode 100644 index 00000000..21213bfd Binary files /dev/null and b/modules/photoannotation/images/select.png differ diff --git a/modules/photoannotation/images/select2.png b/modules/photoannotation/images/select2.png new file mode 100644 index 00000000..2cd2cabe Binary files /dev/null and b/modules/photoannotation/images/select2.png differ diff --git a/modules/photoannotation/images/slider.png b/modules/photoannotation/images/slider.png new file mode 100644 index 00000000..8b03da96 Binary files /dev/null and b/modules/photoannotation/images/slider.png differ diff --git a/modules/photoannotation/js/colorpicker.js b/modules/photoannotation/js/colorpicker.js new file mode 100644 index 00000000..0c51991c --- /dev/null +++ b/modules/photoannotation/js/colorpicker.js @@ -0,0 +1,484 @@ +/** + * + * Color picker + * Author: Stefan Petre www.eyecon.ro + * + * Dual licensed under the MIT and GPL licenses + * + */ +(function ($) { + var ColorPicker = function () { + var + ids = {}, + inAction, + charMin = 65, + visible, + tpl = '
', + defaults = { + eventName: 'click', + onShow: function () {}, + onBeforeShow: function(){}, + onHide: function () {}, + onChange: function () {}, + onSubmit: function () {}, + color: 'ff0000', + livePreview: true, + flat: false + }, + fillRGBFields = function (hsb, cal) { + var rgb = HSBToRGB(hsb); + $(cal).data('colorpicker').fields + .eq(1).val(rgb.r).end() + .eq(2).val(rgb.g).end() + .eq(3).val(rgb.b).end(); + }, + fillHSBFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(4).val(hsb.h).end() + .eq(5).val(hsb.s).end() + .eq(6).val(hsb.b).end(); + }, + fillHexFields = function (hsb, cal) { + $(cal).data('colorpicker').fields + .eq(0).val(HSBToHex(hsb)).end(); + }, + setSelector = function (hsb, cal) { + $(cal).data('colorpicker').selector.css('backgroundColor', '#' + HSBToHex({h: hsb.h, s: 100, b: 100})); + $(cal).data('colorpicker').selectorIndic.css({ + left: parseInt(150 * hsb.s/100, 10), + top: parseInt(150 * (100-hsb.b)/100, 10) + }); + }, + setHue = function (hsb, cal) { + $(cal).data('colorpicker').hue.css('top', parseInt(150 - 150 * hsb.h/360, 10)); + }, + setCurrentColor = function (hsb, cal) { + $(cal).data('colorpicker').currentColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + setNewColor = function (hsb, cal) { + $(cal).data('colorpicker').newColor.css('backgroundColor', '#' + HSBToHex(hsb)); + }, + keyDown = function (ev) { + var pressedKey = ev.charCode || ev.keyCode || -1; + if ((pressedKey > charMin && pressedKey <= 90) || pressedKey == 32) { + return false; + } + var cal = $(this).parent().parent(); + if (cal.data('colorpicker').livePreview === true) { + change.apply(this); + } + }, + change = function (ev) { + var cal = $(this).parent().parent(), col; + if (this.parentNode.className.indexOf('_hex') > 0) { + cal.data('colorpicker').color = col = HexToHSB(fixHex(this.value)); + } else if (this.parentNode.className.indexOf('_hsb') > 0) { + cal.data('colorpicker').color = col = fixHSB({ + h: parseInt(cal.data('colorpicker').fields.eq(4).val(), 10), + s: parseInt(cal.data('colorpicker').fields.eq(5).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(6).val(), 10) + }); + } else { + cal.data('colorpicker').color = col = RGBToHSB(fixRGB({ + r: parseInt(cal.data('colorpicker').fields.eq(1).val(), 10), + g: parseInt(cal.data('colorpicker').fields.eq(2).val(), 10), + b: parseInt(cal.data('colorpicker').fields.eq(3).val(), 10) + })); + } + if (ev) { + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + } + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + cal.data('colorpicker').onChange.apply(cal, [col, HSBToHex(col), HSBToRGB(col)]); + }, + blur = function (ev) { + var cal = $(this).parent().parent(); + cal.data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + }, + focus = function () { + charMin = this.parentNode.className.indexOf('_hex') > 0 ? 70 : 65; + $(this).parent().parent().data('colorpicker').fields.parent().removeClass('colorpicker_focus'); + $(this).parent().addClass('colorpicker_focus'); + }, + downIncrement = function (ev) { + var field = $(this).parent().find('input').focus(); + var current = { + el: $(this).parent().addClass('colorpicker_slider'), + max: this.parentNode.className.indexOf('_hsb_h') > 0 ? 360 : (this.parentNode.className.indexOf('_hsb') > 0 ? 100 : 255), + y: ev.pageY, + field: field, + val: parseInt(field.val(), 10), + preview: $(this).parent().parent().data('colorpicker').livePreview + }; + $(document).bind('mouseup', current, upIncrement); + $(document).bind('mousemove', current, moveIncrement); + }, + moveIncrement = function (ev) { + ev.data.field.val(Math.max(0, Math.min(ev.data.max, parseInt(ev.data.val + ev.pageY - ev.data.y, 10)))); + if (ev.data.preview) { + change.apply(ev.data.field.get(0), [true]); + } + return false; + }, + upIncrement = function (ev) { + change.apply(ev.data.field.get(0), [true]); + ev.data.el.removeClass('colorpicker_slider').find('input').focus(); + $(document).unbind('mouseup', upIncrement); + $(document).unbind('mousemove', moveIncrement); + return false; + }, + downHue = function (ev) { + var current = { + cal: $(this).parent(), + y: $(this).offset().top + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upHue); + $(document).bind('mousemove', current, moveHue); + }, + moveHue = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(4) + .val(parseInt(360*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.y))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upHue = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upHue); + $(document).unbind('mousemove', moveHue); + return false; + }, + downSelector = function (ev) { + var current = { + cal: $(this).parent(), + pos: $(this).offset() + }; + current.preview = current.cal.data('colorpicker').livePreview; + $(document).bind('mouseup', current, upSelector); + $(document).bind('mousemove', current, moveSelector); + }, + moveSelector = function (ev) { + change.apply( + ev.data.cal.data('colorpicker') + .fields + .eq(6) + .val(parseInt(100*(150 - Math.max(0,Math.min(150,(ev.pageY - ev.data.pos.top))))/150, 10)) + .end() + .eq(5) + .val(parseInt(100*(Math.max(0,Math.min(150,(ev.pageX - ev.data.pos.left))))/150, 10)) + .get(0), + [ev.data.preview] + ); + return false; + }, + upSelector = function (ev) { + fillRGBFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + fillHexFields(ev.data.cal.data('colorpicker').color, ev.data.cal.get(0)); + $(document).unbind('mouseup', upSelector); + $(document).unbind('mousemove', moveSelector); + return false; + }, + enterSubmit = function (ev) { + $(this).addClass('colorpicker_focus'); + }, + leaveSubmit = function (ev) { + $(this).removeClass('colorpicker_focus'); + }, + clickSubmit = function (ev) { + var cal = $(this).parent(); + var col = cal.data('colorpicker').color; + cal.data('colorpicker').origColor = col; + setCurrentColor(col, cal.get(0)); + cal.data('colorpicker').onSubmit(col, HSBToHex(col), HSBToRGB(col), cal.data('colorpicker').el); + }, + show = function (ev) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').onBeforeShow.apply(this, [cal.get(0)]); + var pos = $(this).offset(); + var viewPort = getViewport(); + var top = pos.top + this.offsetHeight; + var left = pos.left; + if (top + 176 > viewPort.t + viewPort.h) { + top -= this.offsetHeight + 176; + } + if (left + 356 > viewPort.l + viewPort.w) { + left -= 356; + } + cal.css({left: left + 'px', top: top + 'px'}); + if (cal.data('colorpicker').onShow.apply(this, [cal.get(0)]) != false) { + cal.show(); + } + $(document).bind('mousedown', {cal: cal}, hide); + return false; + }, + hide = function (ev) { + if (!isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) { + if (ev.data.cal.data('colorpicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) { + ev.data.cal.hide(); + } + $(document).unbind('mousedown', hide); + } + }, + isChildOf = function(parentEl, el, container) { + if (parentEl == el) { + return true; + } + if (parentEl.contains) { + return parentEl.contains(el); + } + if ( parentEl.compareDocumentPosition ) { + return !!(parentEl.compareDocumentPosition(el) & 16); + } + var prEl = el.parentNode; + while(prEl && prEl != container) { + if (prEl == parentEl) + return true; + prEl = prEl.parentNode; + } + return false; + }, + getViewport = function () { + var m = document.compatMode == 'CSS1Compat'; + return { + l : window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft), + t : window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop), + w : window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth), + h : window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight) + }; + }, + fixHSB = function (hsb) { + return { + h: Math.min(360, Math.max(0, hsb.h)), + s: Math.min(100, Math.max(0, hsb.s)), + b: Math.min(100, Math.max(0, hsb.b)) + }; + }, + fixRGB = function (rgb) { + return { + r: Math.min(255, Math.max(0, rgb.r)), + g: Math.min(255, Math.max(0, rgb.g)), + b: Math.min(255, Math.max(0, rgb.b)) + }; + }, + fixHex = function (hex) { + var len = 6 - hex.length; + if (len > 0) { + var o = []; + for (var i=0; i -1) ? hex.substring(1) : hex), 16); + return {r: hex >> 16, g: (hex & 0x00FF00) >> 8, b: (hex & 0x0000FF)}; + }, + HexToHSB = function (hex) { + return RGBToHSB(HexToRGB(hex)); + }, + RGBToHSB = function (rgb) { + var hsb = { + h: 0, + s: 0, + b: 0 + }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + if (max != 0) { + + } + hsb.s = max != 0 ? 255 * delta / max : 0; + if (hsb.s != 0) { + if (rgb.r == max) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if (rgb.g == max) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if (hsb.h < 0) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }, + HSBToRGB = function (hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s == 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255-s)*v/255; + var t3 = (t1-t2)*(h%60)/60; + if(h==360) h = 0; + if(h<60) {rgb.r=t1; rgb.b=t2; rgb.g=t2+t3} + else if(h<120) {rgb.g=t1; rgb.b=t2; rgb.r=t1-t3} + else if(h<180) {rgb.g=t1; rgb.r=t2; rgb.b=t2+t3} + else if(h<240) {rgb.b=t1; rgb.r=t2; rgb.g=t1-t3} + else if(h<300) {rgb.b=t1; rgb.g=t2; rgb.r=t2+t3} + else if(h<360) {rgb.r=t1; rgb.g=t2; rgb.b=t1-t3} + else {rgb.r=0; rgb.g=0; rgb.b=0} + } + return {r:Math.round(rgb.r), g:Math.round(rgb.g), b:Math.round(rgb.b)}; + }, + RGBToHex = function (rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function (nr, val) { + if (val.length == 1) { + hex[nr] = '0' + val; + } + }); + return hex.join(''); + }, + HSBToHex = function (hsb) { + return RGBToHex(HSBToRGB(hsb)); + }, + restoreOriginal = function () { + var cal = $(this).parent(); + var col = cal.data('colorpicker').origColor; + cal.data('colorpicker').color = col; + fillRGBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + setSelector(col, cal.get(0)); + setHue(col, cal.get(0)); + setNewColor(col, cal.get(0)); + }; + return { + init: function (opt) { + opt = $.extend({}, defaults, opt||{}); + if (typeof opt.color == 'string') { + opt.color = HexToHSB(opt.color); + } else if (opt.color.r != undefined && opt.color.g != undefined && opt.color.b != undefined) { + opt.color = RGBToHSB(opt.color); + } else if (opt.color.h != undefined && opt.color.s != undefined && opt.color.b != undefined) { + opt.color = fixHSB(opt.color); + } else { + return this; + } + return this.each(function () { + if (!$(this).data('colorpickerId')) { + var options = $.extend({}, opt); + options.origColor = opt.color; + var id = 'collorpicker_' + parseInt(Math.random() * 1000); + $(this).data('colorpickerId', id); + var cal = $(tpl).attr('id', id); + if (options.flat) { + cal.appendTo(this).show(); + } else { + cal.appendTo(document.body); + } + options.fields = cal + .find('input') + .bind('keyup', keyDown) + .bind('change', change) + .bind('blur', blur) + .bind('focus', focus); + cal + .find('span').bind('mousedown', downIncrement).end() + .find('>div.colorpicker_current_color').bind('click', restoreOriginal); + options.selector = cal.find('div.colorpicker_color').bind('mousedown', downSelector); + options.selectorIndic = options.selector.find('div div'); + options.el = this; + options.hue = cal.find('div.colorpicker_hue div'); + cal.find('div.colorpicker_hue').bind('mousedown', downHue); + options.newColor = cal.find('div.colorpicker_new_color'); + options.currentColor = cal.find('div.colorpicker_current_color'); + cal.data('colorpicker', options); + cal.find('div.colorpicker_submit') + .bind('mouseenter', enterSubmit) + .bind('mouseleave', leaveSubmit) + .bind('click', clickSubmit); + fillRGBFields(options.color, cal.get(0)); + fillHSBFields(options.color, cal.get(0)); + fillHexFields(options.color, cal.get(0)); + setHue(options.color, cal.get(0)); + setSelector(options.color, cal.get(0)); + setCurrentColor(options.color, cal.get(0)); + setNewColor(options.color, cal.get(0)); + if (options.flat) { + cal.css({ + position: 'relative', + display: 'block' + }); + } else { + $(this).bind(options.eventName, show); + } + } + }); + }, + showPicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + show.apply(this); + } + }); + }, + hidePicker: function() { + return this.each( function () { + if ($(this).data('colorpickerId')) { + $('#' + $(this).data('colorpickerId')).hide(); + } + }); + }, + setColor: function(col) { + if (typeof col == 'string') { + col = HexToHSB(col); + } else if (col.r != undefined && col.g != undefined && col.b != undefined) { + col = RGBToHSB(col); + } else if (col.h != undefined && col.s != undefined && col.b != undefined) { + col = fixHSB(col); + } else { + return this; + } + return this.each(function(){ + if ($(this).data('colorpickerId')) { + var cal = $('#' + $(this).data('colorpickerId')); + cal.data('colorpicker').color = col; + cal.data('colorpicker').origColor = col; + fillRGBFields(col, cal.get(0)); + fillHSBFields(col, cal.get(0)); + fillHexFields(col, cal.get(0)); + setHue(col, cal.get(0)); + setSelector(col, cal.get(0)); + setCurrentColor(col, cal.get(0)); + setNewColor(col, cal.get(0)); + } + }); + } + }; + }(); + $.fn.extend({ + ColorPicker: ColorPicker.init, + ColorPickerHide: ColorPicker.hidePicker, + ColorPickerShow: ColorPicker.showPicker, + ColorPickerSetColor: ColorPicker.setColor + }); +})(jQuery); diff --git a/modules/photoannotation/js/jquery.annotate.js b/modules/photoannotation/js/jquery.annotate.js new file mode 100644 index 00000000..ba98339e --- /dev/null +++ b/modules/photoannotation/js/jquery.annotate.js @@ -0,0 +1,590 @@ +/// +(function($) { + + $.fn.annotateImage = function(options) { + /// + /// Creates annotations on the given image. + /// Images are loaded from the "getUrl" propety passed into the options. + /// + var opts = $.extend({}, $.fn.annotateImage.defaults, options); + var image = this; + + this.image = this; + this.mode = 'view'; + + // Assign defaults + this.getUrl = opts.getUrl; + this.saveUrl = opts.saveUrl; + this.deleteUrl = opts.deleteUrl; + this.deleteUrl = opts.deleteUrl; + this.editable = opts.editable; + this.useAjax = opts.useAjax; + this.tags = opts.tags; + this.notes = opts.notes; + this.labels = opts.labels; + this.csrf = opts.csrf; + this.cssaclass = opts.cssaclass; + this.rtlsupport = opts.rtlsupport; + this.users = opts.users + + // Add the canvas + this.canvas = $('
'); + this.canvas.children('.image-annotate-edit').hide(); + this.canvas.children('.image-annotate-view').hide(); + this.image.after(this.canvas); + + // Give the canvas and the container their size and background + this.canvas.height(this.height()); + this.canvas.width(this.width()); + this.canvas.css('background-image', 'url("' + this.attr('src') + '")'); + this.canvas.children('.image-annotate-view, .image-annotate-edit').height(this.height()); + this.canvas.children('.image-annotate-view, .image-annotate-edit').width(this.width()); + + // Add the behavior: hide/show the notes when hovering the picture + this.canvas.hover(function() { + if ($(this).children('.image-annotate-edit').css('display') == 'none') { + $(this).children('.image-annotate-view').show(); + } + }, function() { + $(this).children('.image-annotate-view').hide(); + $(this).children('.image-annotate-note').hide(); + }); + + this.canvas.children('.image-annotate-view').hover(function() { + $(this).show(); + }, function() { + $(this).hide(); + $(this).children('.image-annotate-note').hide(); + }); + + // load the notes + if (this.useAjax) { + $.fn.annotateImage.ajaxLoad(this); + } else { + $.fn.annotateImage.load(this, this.labels, this.editable, this.csrf, this.deleteUrl, this.tags, this.saveUrl, this.cssaclass, this.rtlsupport, this.users); + } + + // Add the "Add a note" button + if ($('#g-photoannotation-link').length != 0) { + this.button = $('#g-photoannotation-link'); + this.button.click(function() { + $.fn.annotateImage.add(image, opts.tags, opts.labels, opts.saveUrl, opts.csrf, opts.rtlsupport, opts.users); + }); + //this.canvas.after(this.button); + } + + // Hide the original + this.hide(); + + return this; + }; + + /** + * Plugin Defaults + **/ + $.fn.annotateImage.defaults = { + getUrl: 'your-get.rails', + saveUrl: 'your-save.rails', + deleteUrl: 'your-delete.rails', + editable: true, + useAjax: true, + tags: new Array(), + notes: new Array() + }; + + $.fn.annotateImage.clear = function(image) { + /// + /// Clears all existing annotations from the image. + /// + for (var i = 0; i < image.notes.length; i++) { + image.notes[image.notes[i]].destroy(); + } + image.notes = new Array(); + }; + + $.fn.annotateImage.ajaxLoad = function(image) { + /// + /// Loads the annotations from the "getUrl" property passed in on the + /// options object. + /// + $.getJSON(image.getUrl + '?ticks=' + $.fn.annotateImage.getTicks(), function(data) { + image.notes = data; + $.fn.annotateImage.load(image); + }); + }; + + $.fn.annotateImage.load = function(image, labels, editable, csrf, deleteUrl, tags, saveUrl, cssaclass, rtlsupport, users) { + /// + /// Loads the annotations from the notes property passed in on the + /// options object. + /// + for (var i = 0; i < image.notes.length; i++) { + image.notes[image.notes[i]] = new $.fn.annotateView(image, image.notes[i], tags, labels, editable, csrf, deleteUrl, saveUrl, cssaclass, rtlsupport, users); + } + }; + + $.fn.annotateImage.getTicks = function() { + /// + /// Gets a count og the ticks for the current date. + /// This is used to ensure that URLs are always unique and not cached by the browser. + /// + var now = new Date(); + return now.getTime(); + }; + + $.fn.annotateImage.add = function(image, tags, labels, saveUrl, csrf, rtlsupport, users) { + /// + /// Adds a note to the image. + /// + if (image.mode == 'view') { + image.mode = 'edit'; + + // Create/prepare the editable note elements + var editable = new $.fn.annotateEdit(image, null, tags, labels, saveUrl, csrf, rtlsupport, users); + + $.fn.annotateImage.createSaveButton(editable, image, null, rtlsupport, labels); + $.fn.annotateImage.createCancelButton(editable, image, rtlsupport, labels); + } + }; + + $.fn.annotateImage.createSaveButton = function(editable, image, note, rtlsupport, labels) { + /// + /// Creates a Save button on the editable note. + /// + var ok = $('' + labels[8] + ''); + + ok.click(function() { + var form = $('#image-annotate-edit-form form'); + var text = $('#image-annotate-text').val(); + $.fn.annotateImage.appendPosition(form, editable) + image.mode = 'view'; + + form.submit(); + + editable.destroy(); + }); + editable.form.append(ok); + }; + + $.fn.annotateImage.createCancelButton = function(editable, image, rtlsupport, labels) { + /// + /// Creates a Cancel button on the editable note. + /// + var cancel = $('' + labels[9] + ''); + cancel.click(function() { + editable.destroy(); + image.mode = 'view'; + location.reload(); + }); + editable.form.append(cancel); + }; + + $.fn.annotateImage.saveAsHtml = function(image, target) { + var element = $(target); + var html = ""; + for (var i = 0; i < image.notes.length; i++) { + html += $.fn.annotateImage.createHiddenField("text_" + i, image.notes[i].text); + html += $.fn.annotateImage.createHiddenField("top_" + i, image.notes[i].top); + html += $.fn.annotateImage.createHiddenField("left_" + i, image.notes[i].left); + html += $.fn.annotateImage.createHiddenField("height_" + i, image.notes[i].height); + html += $.fn.annotateImage.createHiddenField("width_" + i, image.notes[i].width); + } + element.html(html); + }; + + $.fn.annotateImage.createHiddenField = function(name, value) { + return '<input type="hidden" name="' + name + '" value="' + value + '" />
'; + }; + + $.fn.annotateEdit = function(image, note, tags, labels, saveUrl, csrf, rtlsupport, users) { + /// + /// Defines an editable annotation area. + /// + this.image = image; + + if (note) { + this.note = note; + } else { + var newNote = new Object(); + newNote.noteid = "new"; + newNote.top = 30; + newNote.left = 30; + newNote.width = 30; + newNote.height = 30; + newNote.text = ""; + newNote.description = ""; + newNote.notetype = ""; + this.note = newNote; + } + + // Set area + var area = image.canvas.children('.image-annotate-edit').children('.image-annotate-edit-area'); + this.area = area; + this.area.css('height', this.note.height + 'px'); + this.area.css('width', this.note.width + 'px'); + this.area.css('left', this.note.left + 'px'); + this.area.css('top', this.note.top + 'px'); + + // Show the edition canvas and hide the view canvas + image.canvas.children('.image-annotate-view').hide(); + image.canvas.children('.image-annotate-edit').show(); + + // Add the note (which we'll load with the form afterwards) + var selectedtag = ""; + var notetitle = ""; + var username = ""; + var selecteduser = ""; + if (this.note.notetype == "face") { + selectedtag = this.note.text; + selecteduser = " selected=\"selected\""; + } else if (this.note.notetype == "user") { + username = this.note.text; + } else { + notetitle = this.note.text; + selecteduser = " selected=\"selected\""; + } + var userdropdown = ''; + var form = $('
\ +
\ + \ + \ +
' + labels[12] + '\ + ' + userdropdown + + '
' + labels[4] + '
\ + \ + ' + + '
' + labels[4] + '
\ + \ +
\ +
' + labels[2] + '\ +
'); + + + + this.form = form; + $('body').append(this.form); + $("#photoannotation-form").ready(function() { + var url = tags; + $("input#image-annotate-tag-text").autocomplete( + url, { + max: 30, + multiple: false, + cacheLength: 1 + } + ); + }); + $("input#image-annotate-tag-text").change(function() { + if ($("input#image-annotate-tag-text").val() != "") { + $("input#image-annotate-text").html(""); + $("input#image-annotate-text").val(""); + $("input#image-annotate-text").text(""); + $("select#photoannotation-user-list").val('-1'); + } + }); + $("input#image-annotate-text").keyup(function() { + if ($("input#image-annotate-text").val() != "") { + $("input#image-annotate-tag-text").html(""); + $("input#image-annotate-tag-text").val(""); + $("input#image-annotate-tag-text").text(""); + $("select#photoannotation-user-list").val('-1'); + } + }); + $("select#photoannotation-user-list").keyup(function() { + if ($("select#photoannotation-user-list").val() != "-1") { + $("input#image-annotate-tag-text").html(""); + $("input#image-annotate-tag-text").val(""); + $("input#image-annotate-tag-text").text(""); + $("input#image-annotate-text").html(""); + $("input#image-annotate-text").val(""); + $("input#image-annotate-text").text(""); + } + }); + this.form.css('left', this.area.offset().left + 'px'); + this.form.css('top', (parseInt(this.area.offset().top) + parseInt(this.area.height()) + 7) + 'px'); + + // Set the area as a draggable/resizable element contained in the image canvas. + // Would be better to use the containment option for resizable but buggy + area.resizable({ + handles: 'all', + + start: function(e, ui) { + form.hide(); + }, + stop: function(e, ui) { + form.css('left', area.offset().left + 'px'); + form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 7) + 'px'); + form.show(); + } + }) + .draggable({ + containment: image.canvas, + drag: function(e, ui) { + form.hide(); + }, + stop: function(e, ui) { + form.css('left', area.offset().left + 'px'); + form.css('top', (parseInt(area.offset().top) + parseInt(area.height()) + 7) + 'px'); + form.show(); + } + }); + return this; + }; + + $.fn.annotateEdit.prototype.destroy = function() { + /// + /// Destroys an editable annotation area. + /// + this.image.canvas.children('.image-annotate-edit').hide(); + this.area.resizable('destroy'); + this.area.draggable('destroy'); + this.area.css('height', ''); + this.area.css('width', ''); + this.area.css('left', ''); + this.area.css('top', ''); + this.form.remove(); + } + + $.fn.annotateView = function(image, note, tags, labels, editable, csrf, deleteUrl, saveUrl, cssaclass, rtlsupport, users) { + /// + /// Defines a annotation area. + /// + this.image = image; + + this.note = note; + + // Add the area + this.area = $('
'); + image.canvas.children('.image-annotate-view').prepend(this.area); + + if (editable) { + this.delarea = $('
'); + this.editarea = $('
'); + image.canvas.children('.image-annotate-view').prepend(this.delarea); + image.canvas.children('.image-annotate-view').prepend(this.editarea); + this.delarea.bind('click',function () { + var alink = $(cssaclass); + alink.unbind(); + alink.attr ('href', '#'); + alink.removeAttr ('rel'); + var confdialog = '
' + labels[3] + '
'; + $('body').append(confdialog); + var btns = {}; + if (rtlsupport == "") { + diagclass = "inmage-annotate-dialog"; + } else { + diagclass = "inmage-annotate-dialog-rtl"; + } + btns[labels[5]] = function(){ var delform = $(this).attr("rel"); $("form#" + delform).submit(); }; + btns[labels[6]] = function(){ location.reload(); }; + $('#image-annotate-conf-dialog').dialog({ + modal: true, + resizable: false, + dialogClass: diagclass, + title: labels[7], + close: function(event, ui) { location.reload(); }, + buttons: btns + }); + }) + var form = this; + this.editarea.bind('click',function () { + var alink = $(cssaclass); + alink.unbind(); + alink.attr ('href', '#'); + alink.removeAttr ('rel'); + form.edit(tags, labels, saveUrl, csrf, rtlsupport, users); + }) + this.delarea.hide(); + this.editarea.hide(); + } + + // Add the note + var notedescription = ""; + if (note.description != "") { + notedescription = "
" + note.description; + } + this.form = $('
' + note.text + notedescription + '
'); + this.form.hide(); + image.canvas.children('.image-annotate-view').append(this.form); + this.form.children('span.actions').hide(); + + // Set the position and size of the note + this.setPosition(rtlsupport); + + // Add the behavior: hide/display the note when hovering the area + var annotation = this; + this.area.hover(function() { + annotation.show(); + if (annotation.delarea != undefined) { + annotation.delarea.show(); + annotation.editarea.show(); + } + }, function() { + annotation.hide(); + if (annotation.delarea != undefined) { + annotation.delarea.hide(); + annotation.editarea.hide(); + } + }); + var legendspan = "#photoannotation-legend-" + this.note.notetype + "-" + this.note.noteid; + if ($(legendspan).length > 0) { + $(legendspan).hover(function() { + var legendsarea = "#photoannotation-area-" + note.notetype + "-" + note.noteid; + $(legendsarea).children('.image-annotate-view').show(); + $(".image-annotate-view").show(); + $(legendsarea).show(); + annotation.show(); + }, function() { + var legendsarea = "#photoannotation-area-" + note.notetype + "-" + note.noteid; + annotation.hide(); + $(legendsarea).children('.image-annotate-view').hide(); + $(".image-annotate-view").hide(); + }); + } + if (editable) { + this.delarea.hover(function() { + annotation.delarea.show(); + annotation.editarea.show(); + }, function() { + annotation.delarea.hide(); + annotation.editarea.hide(); + }); + this.editarea.hover(function() { + annotation.delarea.show(); + annotation.editarea.show(); + }, function() { + annotation.delarea.hide(); + annotation.editarea.hide(); + }); + } + // Edit a note feature + if (note.url != "" && note.url != null) { + this.area.bind('click',function () { + var alink = $(cssaclass); + alink.unbind(); + alink.attr ('href', '#'); + alink.removeAttr ('rel'); + window.location = note.url; + }) + } + }; + + $.fn.annotateView.prototype.setPosition = function(rtlsupport) { + /// + /// Sets the position of an annotation. + /// + this.area.children('div').height((parseInt(this.note.height) - 2) + 'px'); + this.area.children('div').width((parseInt(this.note.width) - 2) + 'px'); + this.area.css('left', (this.note.left) + 'px'); + this.area.css('top', (this.note.top) + 'px'); + this.form.css('left', (this.note.left) + 'px'); + this.form.css('top', (parseInt(this.note.top) + parseInt(this.note.height) + 7) + 'px'); + + if (this.delarea != undefined) { + this.delarea.children('div').height('14px'); + this.delarea.children('div').width('14px'); + this.delarea.css('top', (this.note.top) + 'px'); + this.editarea.children('div').height('14px'); + this.editarea.children('div').width('14px'); + if (rtlsupport == '') { + this.delarea.css('left', (this.note.left + parseInt(this.note.width)) + 'px'); + this.editarea.css('left', (this.note.left + parseInt(this.note.width)) + 'px'); + } else { + this.delarea.css('left', (this.note.left - 16) + 'px'); + this.editarea.css('left', (this.note.left - 16) + 'px'); + } + this.editarea.css('top', (this.note.top + 16) + 'px'); + } + }; + + $.fn.annotateView.prototype.show = function() { + /// + /// Highlights the annotation + /// + this.form.fadeIn(250); + if (!this.note.editable) { + this.area.addClass('image-annotate-area-hover'); + } else { + this.area.addClass('image-annotate-area-editable-hover'); + } + }; + + $.fn.annotateView.prototype.hide = function() { + /// + /// Removes the highlight from the annotation. + /// + this.form.fadeOut(250); + this.area.removeClass('image-annotate-area-hover'); + this.area.removeClass('image-annotate-area-editable-hover'); + }; + + $.fn.annotateView.prototype.destroy = function() { + /// + /// Destroys the annotation. + /// + this.area.remove(); + this.form.remove(); + } + + $.fn.annotateView.prototype.edit = function(tags, labels, saveUrl, csrf, rtlsupport, users) { + /// + /// Edits the annotation. + /// + if (this.image.mode == 'view') { + this.image.mode = 'edit'; + var annotation = this; + + // Create/prepare the editable note elements + var editable = new $.fn.annotateEdit(this.image, this.note, tags, labels, saveUrl, csrf, rtlsupport, users); + $.fn.annotateImage.createSaveButton(editable, this.image, annotation, rtlsupport, labels); + $.fn.annotateImage.createCancelButton(editable, this.image, rtlsupport, labels); + } + }; + + $.fn.annotateImage.appendPosition = function(form, editable) { + /// + /// Appends the annotations coordinates to the given form that is posted to the server. + /// + var areaFields = $('' + + '' + + '' + + '' + + ''); + form.append(areaFields); + } + + $.fn.annotateView.prototype.resetPosition = function(editable, text) { + /// + /// Sets the position of an annotation. + /// + this.form.html(text); + this.form.hide(); + + // Resize + this.area.children('div').height(editable.area.height() + 'px'); + this.area.children('div').width((editable.area.width() - 2) + 'px'); + this.area.css('left', (editable.area.position().left) + 'px'); + this.area.css('top', (editable.area.position().top) + 'px'); + this.form.css('left', (editable.area.position().left) + 'px'); + this.form.css('top', (parseInt(editable.area.position().top) + parseInt(editable.area.height()) + 7) + 'px'); + + // Save new position to note + this.note.top = editable.area.position().top; + this.note.left = editable.area.position().left; + this.note.height = editable.area.height(); + this.note.width = editable.area.width(); + this.note.text = text; + this.note.id = editable.note.id; + this.editable = true; + }; + +})(jQuery); diff --git a/modules/photoannotation/models/items_face.php b/modules/photoannotation/models/items_face.php new file mode 100644 index 00000000..8f3a4072 --- /dev/null +++ b/modules/photoannotation/models/items_face.php @@ -0,0 +1,21 @@ + +
+

+

+

TagFaces module by rWatcher.
+ This means that notes and faces that you create in either one will be shown and are editable by the other module as well. If you added users to an annotation area though they will only be displayed with the Photo Annotation module.
+ You cannot have both active at the same time.", array("tagfaces" => "http://codex.gallery2.org/Gallery3:Modules:tagfaces")) ?> +

Convert existing tag annotations to user annotations", array("url" => url::site("admin/photoannotation/converter/"))) ?> +
Check for orphaned annotations", array("url" => url::site("admin/photoannotation/tagsmaintanance/"))) ?>

+ +
+ diff --git a/modules/photoannotation/views/admin_photoannotation_converter.html.php b/modules/photoannotation/views/admin_photoannotation_converter.html.php new file mode 100644 index 00000000..4b8dfa1d --- /dev/null +++ b/modules/photoannotation/views/admin_photoannotation_converter.html.php @@ -0,0 +1,10 @@ + +
+

+

+



+ +
Back to photo annotation settings", array("url" => url::site("admin/photoannotation/"))) ?> +
Check for orphaned annotations", array("url" => url::site("admin/photoannotation/tagsmaintanance/"))) ?>

+ +
diff --git a/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php b/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php new file mode 100644 index 00000000..9e373780 --- /dev/null +++ b/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php @@ -0,0 +1,27 @@ + +
+

+

+

+

Back to photo annotation settings", array("url" => url::site("admin/photoannotation/"))) ?> +
Convert existing tag annotations to user annotations", array("url" => url::site("admin/photoannotation/converter/"))) ?>

+ +

+

$user_orphanes_deleted)) ?> +
$tag_orpanes_deleted)) ?> +
$item_orphanes_deleted)) ?> +

+ Back", array("url" => url::site("admin/photoannotation/"))) ?> + +

+

$user_orphanes_count)) ?> +
$tag_orpanes_count)) ?> +
$item_orphanes_count)) ?> +

+ + Back", array("url" => url::site("admin/photoannotation/"))) ?> + + Remove all", array("url" => url::site("admin/photoannotation/tagsmaintanance/true"))) ?> + + +
diff --git a/modules/photoannotation/views/photoannotation_highlight_block.html.php b/modules/photoannotation/views/photoannotation_highlight_block.html.php new file mode 100644 index 00000000..1e61bda6 --- /dev/null +++ b/modules/photoannotation/views/photoannotation_highlight_block.html.php @@ -0,0 +1,151 @@ +where("item_id", "=", $item->id) + ->find_all(); + $existingFaces = ORM::factory("items_face") + ->where("item_id", "=", $item->id) + ->find_all(); + $existingNotes = ORM::factory("items_note") + ->where("item_id", "=", $item->id) + ->find_all(); + $users = ORM::factory("user")->order_by("name", "ASC")->find_all(); + $fullname = module::get_var("photoannotation", "fullname", false); + $showusers = module::get_var("photoannotation", "showusers", false); + $showfaces = module::get_var("photoannotation", "showfaces", false); + $shownotes = module::get_var("photoannotation", "shownotes", false); + if (locales::is_rtl()) { + $rtl_support = "image-annotate-rtl"; + } else { + $rtl_support = ""; + } + $tags_arraystring = ""; + $jscode = ""; + $legend_faces = ""; + $legend_notes = ""; + if (module::get_var("gallery", "active_site_theme") == "greydragon") { + $css_item_id = "#g-photo-id-". $item->id; + $css_a_class = ".g-sb-preview"; + } else { + $css_item_id = "#g-item-id-". $item->id; + $css_a_class = ".g-fullsize-link"; + } + // If it does, then insert some javascript and display an image map + // to show where the faces are at. + if ((count($existingFaces) > 0) || (count($existingNotes) > 0) || (count($existingUsers) > 0)) { + $jscode = "notes: [ "; + foreach ($existingUsers as $oneUser) { + $oneTag = ORM::factory("user", $oneUser->user_id); + if ($oneTag->loaded()) { + if ($fullname && ($oneTag->full_name != "")) { + $user_text = $oneTag->full_name; + } else { + $user_text = $oneTag->name; + } + if ($showusers) { + $legend_users .= "id . "\">user_id) ."\">". html::clean($user_text) .", "; + } + $jscode .= "{ \"top\": ". $oneUser->y1 .",\n"; + $jscode .= "\"left\": ". $oneUser->x1 .",\n"; + $jscode .= "\"width\": ". ($oneUser->x2 - $oneUser->x1) .",\n"; + $jscode .= "\"height\": ". ($oneUser->y2 - $oneUser->y1) .",\n"; + $jscode .= "\"text\": \"". html::clean($user_text) ."\",\n"; + $jscode .= "\"description\": \"". html::clean($oneUser->description) ."\",\n"; + $jscode .= "\"noteid\": ". $oneUser->id .",\n"; + $jscode .= "\"notetype\": \"user\",\n"; + $jscode .= "\"editable\": true,\n"; + $jscode .= "\"url\": \"". user_profile::url($oneUser->user_id) ."\" },\n"; + } + } + if ($legend_users != "") { + $legend_users = trim($legend_users, ", "); + $legend_users = t("Users on this photo: ") . $legend_users; + } + foreach ($existingFaces as $oneFace) { + $oneTag = ORM::factory("tag", $oneFace->tag_id); + if ($oneTag->loaded()) { + if ($showfaces) { + $legend_faces .= "id . "\">url() ."\">". html::clean($oneTag->name) .", "; + } + $jscode .= "{ \"top\": ". $oneFace->y1 .",\n"; + $jscode .= "\"left\": ". $oneFace->x1 .",\n"; + $jscode .= "\"width\": ". ($oneFace->x2 - $oneFace->x1) .",\n"; + $jscode .= "\"height\": ". ($oneFace->y2 - $oneFace->y1) .",\n"; + $jscode .= "\"text\": \"". html::clean($oneTag->name) ."\",\n"; + $jscode .= "\"description\": \"". html::clean($oneFace->description) ."\",\n"; + $jscode .= "\"noteid\": ". $oneFace->id .",\n"; + $jscode .= "\"notetype\": \"face\",\n"; + $jscode .= "\"editable\": true,\n"; + $jscode .= "\"url\": \"". $oneTag->url() ."\" },\n"; + } + } + if ($legend_faces != "") { + $legend_faces = trim($legend_faces, ", "); + $legend_faces = t("Faces on this photo: ") . $legend_faces; + } + foreach ($existingNotes as $oneNote) { + if ($shownotes) { + $legend_notes .= "id . "\">". html::clean($oneNote->title) .", "; + } + $jscode .= "{ \"top\": ". $oneNote->y1 .",\n"; + $jscode .= "\"left\": ". $oneNote->x1 .",\n"; + $jscode .= "\"width\": ". ($oneNote->x2 - $oneNote->x1) .",\n"; + $jscode .= "\"height\": ". ($oneNote->y2 - $oneNote->y1) .",\n"; + $jscode .= "\"text\": \"". html::clean($oneNote->title) ."\",\n"; + $jscode .= "\"description\": \"". html::clean($oneNote->description) ."\",\n"; + $jscode .= "\"noteid\": ". $oneNote->id .",\n"; + $jscode .= "\"notetype\": \"note\",\n"; + $jscode .= "\"editable\": false,\n"; + $jscode .= "\"url\": \"\" },\n"; + } + $jscode = trim($jscode, ",\n"); + $jscode .= " ],"; + if ($legend_notes != "") { + $legend_notes = trim($legend_notes, ", "); + $legend_notes = t("Notes on this photo: ") . $legend_notes; + } + } + $legend_display = ""; + if ($legend_users != "" || $legend_faces != "" || $legend_notes != "") { + $legend_display = $legend_users . "
" . $legend_faces . "
" . $legend_notes; + $legend_display = str_replace("

", "
", $legend_display); + } + $users_arraystring = "users: [ "; + foreach ($users as $user) { + if ($fullname && ($user->full_name != "")) { + $user_text = $user->full_name; + } else { + $user_text = $user->name; + } + if ($user->name != "guest") { + $users_arraystring .= "{'name':'". html::clean($user_text) ."','id':'". $user->id ."'},"; + } + } + $users_arraystring = trim($users_arraystring, ","); + $users_arraystring .= " ],"; + $labels_arraystring = "labels: [ '". t("Tag:") ."','". t("Note Title:") ."','". t("Description (optional)") ."','". t("Are you sure you want to delete this annotation?") ."','". t("or") ."','". t("Yes") ."','". t("No") ."','". t("Confirm deletion") ."','". t("Save") ."','". t("Cancel") ."','". t("User:") ."','". t("No user selected") ."','". t("Select one of the following") ."' ],"; +?> + + + ". $legend_display ."
" ?> + diff --git a/modules/register/controllers/register.php b/modules/register/controllers/register.php index 93c183a2..7f79bc63 100755 --- a/modules/register/controllers/register.php +++ b/modules/register/controllers/register.php @@ -146,6 +146,7 @@ class register_Controller extends Controller { ->rules("valid_url"); module::event("register_add_form", $form); + module::event("captcha_protect_form", $form); $group->submit("")->value(t("Register")); return $form; } diff --git a/modules/user_albums/helpers/user_albums_event.php b/modules/user_albums/helpers/user_albums_event.php new file mode 100644 index 00000000..d11e5ac5 --- /dev/null +++ b/modules/user_albums/helpers/user_albums_event.php @@ -0,0 +1,53 @@ +name}"; + $group = identity::lookup_group_by_name($group_name); + if (!$group) { + $group = identity::create_group($group_name); + identity::add_user_to_group($user, $group); + } + + // Create an album for the user, if it doesn't exist + $album = ORM::factory("item") + ->where("parent_id", "=", item::root()->id) + ->where("name", "=", $user->name) + ->find(); + if (!$album->loaded()) { + $album->type = "album"; + $album->name = $user->name; + $album->title = "{$user->name}'s album"; + $album->parent_id = item::root()->id; + $album->sort_column = "weight"; + $album->sort_order = "asc"; + $album->save(); + + access::allow($group, "view", item::root()); + access::allow($group, "view_full", $album); + access::allow($group, "edit", $album); + access::allow($group, "add", $album); + } + } +} diff --git a/modules/user_albums/module.info b/modules/user_albums/module.info new file mode 100644 index 00000000..8e685e8c --- /dev/null +++ b/modules/user_albums/module.info @@ -0,0 +1,3 @@ +name = "User Albums" +description = "Create a personal album for new users" +version = 1 diff --git a/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressdb.php b/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressdb.php index 642eaf61..e71110e6 100644 --- a/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressdb.php +++ b/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressdb.php @@ -205,14 +205,14 @@ class IdentityProvider_Wordpressdb_Driver implements IdentityProvider_Driver { /** * @see IdentityProvider_Driver::add_user_to_group. */ - static function add_user_to_group($user, $group_id) { + function add_user_to_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } /** * @see IdentityProvider_Driver::remove_user_to_group. */ - static function remove_user_from_group($user, $group_id) { + function remove_user_from_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } } // End Identity Gallery Driver @@ -267,7 +267,7 @@ class Wordpress_User implements User_Definition { } $this->admin = false; return $this->admin; - + case "email": return $this->user_info['user_email']; diff --git a/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressfile.php b/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressfile.php index 2cccb1ed..4e2b84d7 100644 --- a/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressfile.php +++ b/modules/wordpress_auth/libraries/drivers/IdentityProvider/Wordpressfile.php @@ -179,14 +179,14 @@ class IdentityProvider_Wordpressfile_Driver implements IdentityProvider_Driver { /** * @see IdentityProvider_Driver::add_user_to_group. */ - static function add_user_to_group($user, $group_id) { + function add_user_to_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } /** * @see IdentityProvider_Driver::remove_user_to_group. */ - static function remove_user_from_group($user, $group_id) { + function remove_user_from_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } } // End Identity Gallery Driver diff --git a/themes/browny_admin_wind/css/screen.css b/themes/browny_admin_wind/css/screen.css index 77ca3853..441a1a3b 100644 --- a/themes/browny_admin_wind/css/screen.css +++ b/themes/browny_admin_wind/css/screen.css @@ -411,26 +411,6 @@ th { background-color: #FFF; } -/* Theme options ~~~~~~~~~~~~~~~~~~~~~~~~ */ -#g-theme-options-form { - border: 1px solid #9e7247; -} -#g-theme-options-form-tabs { - border: none !important; -} -#g-theme-options-form fieldset { - border: none; -} - -.ui-tabs .ui-tabs-nav li a { - padding: 0 1em; -} - -.ui-tabs .ui-tabs-nav li a.g-error { - background: none no-repeat scroll 0 0 transparent; - color: #FF0000 !important; -} - /** ******************************************************************* * 5) Navigation and menus *********************************************************************/ diff --git a/themes/browny_admin_wind/views/admin.html.php b/themes/browny_admin_wind/views/admin.html.php index f9ef18c7..c4b51f41 100644 --- a/themes/browny_admin_wind/views/admin.html.php +++ b/themes/browny_admin_wind/views/admin.html.php @@ -11,7 +11,7 @@ - " type="image/x-icon" /> + " type="image/x-icon" /> css("yui/reset-fonts-grids.css") ?> css("themeroller/ui.base.css") ?> @@ -77,9 +77,11 @@
admin_page_bottom() ?> diff --git a/themes/browny_wind/css/fix-ie.css b/themes/browny_wind/css/fix-ie.css index f7f08486..0633ff07 100644 --- a/themes/browny_wind/css/fix-ie.css +++ b/themes/browny_wind/css/fix-ie.css @@ -1,5 +1,5 @@ /** - * Fix display in IE 6, 7 + * Fix display in IE 6, 7, and 8 */ #g-banner { @@ -7,6 +7,10 @@ zoom: 1; } +#g-sidebar { + overflow: hidden; +} + #g-photo, #g-movie { zoom: 1; @@ -22,8 +26,23 @@ input.submit { display: inline !important; } +.g-short-form input.text, +.g-short-form input.submit { + font-size: 1em; + line-height: 1em; + padding: .38em .3em; +} + +#g-search-form input#q { + width: 300px; +} + #g-add-tag-form input.textbox { - width: 110px; + width: 110px !important; +} + +#g-add-tag-form input[type='submit'] { + padding: .3em 0 !important; } #g-dialog .g-cancel { diff --git a/themes/browny_wind/css/screen.css b/themes/browny_wind/css/screen.css index 3e16f6a6..15c04991 100644 --- a/themes/browny_wind/css/screen.css +++ b/themes/browny_wind/css/screen.css @@ -329,6 +329,32 @@ td { background-color: #fff; } +/* Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#g-edit-permissions-form td { + background-image: none; +} + +#g-edit-permissions-form fieldset { + border: 1px solid #ccc; +} + +#g-permissions .g-denied { + background-color: #fcc; +} + +#g-permissions .g-allowed { + background-color: #fcf9ce; +} + +#g-permissions .g-breadcrumbs a { + border: 1px solid #fff; +} + +#g-permissions .g-active a { + border: 1px solid #ddd; + background: #eee; +} + /** ******************************************************************* * 5) Navigation and menus *********************************************************************/ diff --git a/themes/browny_wind/views/album.html.php b/themes/browny_wind/views/album.html.php index de196be0..1a56af67 100644 --- a/themes/browny_wind/views/album.html.php +++ b/themes/browny_wind/views/album.html.php @@ -16,7 +16,9 @@
  • thumb_top($child) ?> + has_thumb()): ?> thumb_img(array("class" => "g-thumbnail")) ?> + thumb_bottom($child) ?> context_menu($child, "#g-item-id-{$child->id} .g-thumbnail") ?> diff --git a/themes/browny_wind/views/page.html.php b/themes/browny_wind/views/page.html.php index ca29a809..45ad51a7 100644 --- a/themes/browny_wind/views/page.html.php +++ b/themes/browny_wind/views/page.html.php @@ -23,13 +23,13 @@ - " type="image/x-icon" /> + " type="image/x-icon" /> css("yui/reset-fonts-grids.css") ?> css("superfish/css/superfish.css") ?> css("themeroller/ui.base.css") ?> css("gallery.common.css") ?> css("screen.css") ?> - @@ -101,18 +101,23 @@ > - - url($parent->id == $theme->item()->parent_id ? "show={$theme->item()->id}" : null) ?>"> - title) ?> + + title, + module::get_var("gallery", "visible_title_length"))) ?>
  • -
  • ">item()->title) ?>
  • +
  • "> + item()->title, + module::get_var("gallery", "visible_title_length"))) ?> +
  • diff --git a/themes/browny_wind/views/paginator.html.php b/themes/browny_wind/views/paginator.html.php index 5034c965..1f3017ad 100644 --- a/themes/browny_wind/views/paginator.html.php +++ b/themes/browny_wind/views/paginator.html.php @@ -26,11 +26,38 @@ // ?> +parent(); + endif; + $current_page = $position; + $total_pages = $total; + $siblings = $item->parent()->children(); + for ($i = 1; $i <= $total; $i++): + $_pagelist[$i] = $siblings[$i-1]->url(); + endfor; + } + endif; +?> +
    • - + + + + + + + + + + 1): ?> + @@ -39,7 +66,7 @@ - + @@ -67,7 +94,7 @@
    • - + @@ -76,7 +103,17 @@ - + + + + + + + + + + + diff --git a/themes/greydragon/admin/controllers/admin_theme_options.php b/themes/greydragon/admin/controllers/admin_theme_options.php index f83a5560..ec33e6b3 100644 --- a/themes/greydragon/admin/controllers/admin_theme_options.php +++ b/themes/greydragon/admin/controllers/admin_theme_options.php @@ -1,7 +1,7 @@ "g-theme-options-form")); - - $group = $form->group("requirements")->label("Prerequisites checklist"); - $group->checkbox("shadowbox")->label(t("Shadowbox module")) - ->checked((module::is_active("shadowbox")))->disabled(true); + protected $min_gallery_ver = 30; + private function load_theme_info() { $file = THEMEPATH . "greydragon/theme.info"; $theme_info = new ArrayObject(parse_ini_file($file), ArrayObject::ARRAY_AS_PROPS); + return $theme_info; + } - $group = $form->group("edit_theme")->label(t("Grey Dragon Theme") . " - " . t("v.") . $theme_info->version); - $group->input("row_count")->label(t("Rows per album page"))->id("g-page-size") - ->rules("required|valid_digit") - ->value(module::get_var("gallery", "page_size") / 3); + private function get_theme_version() { + $theme_info = $this->load_theme_info(); + return ($theme_info->version); + } - $group->input("resize_size")->label(t("Resized image size (in pixels)"))->id("g-resize-size") + private function get_theme_name() { + $theme_info = $this->load_theme_info(); + return ($theme_info->name); + } + + private function get_colorpacks() { + $colorpacks = array(); + $colorpackroot = THEMEPATH . 'greydragon/css/colorpacks/'; + + foreach (scandir($colorpackroot) as $colorpack_name): + if (file_exists($colorpackroot . "$colorpack_name/colors.css")): + if ($colorpack_name[0] == "."): + continue; + endif; + + $colorpacks[$colorpack_name] = t($colorpack_name); + endif; + endforeach; + return $colorpacks; + } + + private function prerequisite_check($group, $id, $is_ok, $caption, $caption_ok, $caption_failed, $iswarning, $msg_error) { + + $confirmation_caption = ($is_ok)? $caption_ok : $caption_failed; + $checkbox = $group->checkbox($id) + ->label($caption . " " . $confirmation_caption) + ->checked($is_ok) + ->disabled(true); + if ($is_ok): + $checkbox->class("g-success"); + elseif ($iswarning): + $checkbox->class("g-prerequisite g-warning")->error_messages("failed", $msg_error)->add_error("failed", 1); + else: + $checkbox->class("g-error")->error_messages("failed", $msg_error)->add_error("failed", 1); + endif; + } + + protected function get_edit_form_admin() { + $form = new Forge("admin/theme_options/save/", "", null, array("id" =>"g-theme-options-form")); + + $group = $form->group("requirements")->label("Prerequisites Checklist"); + $gallery_ver = module::get_version("gallery"); + + $this->prerequisite_check($group, "vercheck", $gallery_ver >= $this->min_gallery_ver, + t("Gallery 3 Core v.") . $this->min_gallery_ver, "Installed", "Required", FALSE, t("Check Failed. Minimum Required Version") . " " . $gallery_ver); + + $this->prerequisite_check($group, "shadowbox", ((module::is_active("shadowbox")) and (module::info("shadowbox"))), + t("Shadowbox Module"), "Found", "Required", FALSE, t("Check Failed. Shadowbox Module not Installed.")); + + if (!module::get_var("th_greydragon", "hide_thumbmeta")): + $this->prerequisite_check($group, "info", (module::is_active("info") and module::info("info")), + t("Info Module"), "Found", "Required", FALSE, t("Check Failed. Module is required to display Thumb metadata.")); + endif; + + $group = $form->group("recommended")->label("Module Recommendations"); + + $organize_active = ((module::is_active("organize")) and (module::info("organize"))); + $this->prerequisite_check($group, "organizecheck", !$organize_active, + t("Organize Module"), "not Used", "Found", TRUE, t("Default Organize module is active but is not supported in full by the theme.")); + + $kbdnav_active = ((module::is_active("kbd_nav")) and (module::info("kbd_nav"))); + $this->prerequisite_check($group, "kbdnavcheck", $kbdnav_active, + t("Kbd Navigation Module"), "Found", "not Found", TRUE, t('Install module to enable keyboard navigation support.')); + + $sidebar_allowed = module::get_var("th_greydragon", "sidebar_allowed"); + $sidebar_visible = module::get_var("th_greydragon", "sidebar_visible"); + + $pagesize = module::get_var("gallery", "page_size"); + if (($sidebar_allowed == "none") and ($sidebar_visible == "none")): + $pagesize = $pagesize / 4; + else: + $pagesize = $pagesize / 3; + endif; + + $group = $form->group("edit_theme")->label(t("General Settings")); + $group->input("row_count") + ->label(t("Rows per Album Page")) ->rules("required|valid_digit") + ->error_messages("required", t("You must enter a number")) + ->error_messages("valid_digit", t("You must enter a number")) + ->value($pagesize); + $group->input("resize_size") + ->label(t("Resized Image Size (in pixels)")) + ->rules("required|valid_digit") + ->error_messages("required", t("You must enter a number")) + ->error_messages("valid_digit", t("You must enter a number")) ->value(module::get_var("gallery", "resize_size")); - $group->checkbox("build_resize")->label(t("Mark to build all resizes (from Maintenace page)"))->id("g-build-resize")->value(false); - $group->checkbox("build_thumbs")->label(t("Mark to build all thumbnails (200x200) (from Maintenace page)"))->id("g-build-thumb")->value(false); - - $group->checkbox("photonav_top")->label(t("Show top photo navigator")) - ->checked(module::get_var("th_greydragon", "photonav_top")); - $group->checkbox("photonav_bottom")->label(t("Show bottom photo navigator")) - ->checked(module::get_var("th_greydragon", "photonav_bottom")); - - $group->dropdown("sidebar_allowed")->label(t("Allowed SideBar Positions")) - ->options(array("any" => t("Any"), "left" => t("Left"), "right" => t("Right"), "none" => t("None"))) - ->selected(module::get_var("th_greydragon", "sidebar_allowed")); - $group->dropdown("sidebar_visible")->label(t("Default SideBar Position")) - ->options(array("right" => t("Right"), "left" => t("Left"), "none" => t("None"))) - ->selected(module::get_var("th_greydragon", "sidebar_visible")); - - $group->input("header_text")->label(t("Header text"))->id("g-header-text") - ->value(module::get_var("gallery", "header_text")); - $group->input("footer_text")->label(t("Footer text"))->id("g-footer-text") - ->value(module::get_var("gallery", "footer_text")); - $group->checkbox("show_credits")->label(t("Show site credits"))->id("g-footer-text") - ->checked(module::get_var("gallery", "show_credits")); - - $group->input("copyright")->label(t("Copyright message to display on footer"))->id("g-theme-copyright") - ->value(module::get_var("th_greydragon", "copyright")); - $group->input("logo_path")->label(t("URL or path to alternate logo image"))->id("g-site-logo") + $group->input("logo_path") + ->label(t("Alternate Logo Image")) ->value(module::get_var("th_greydragon", "logo_path")); + $group->input("header_text") + ->label(t("Header Text")) + ->value(module::get_var("gallery", "header_text")); + $group->input("footer_text") + ->label(t("Footer Text")) + ->value(module::get_var("gallery", "footer_text")); + $group->input("copyright") + ->label(t("Copyright Message")) + ->value(module::get_var("th_greydragon", "copyright")); + $group->dropdown("colorpack") + ->label(t("Selected Color Pack")) + ->options(self::get_colorpacks()) + ->selected(module::get_var("th_greydragon", "color_pack", "greydragon")); + + $group = $form->group("edit_theme_adv_main")->label(t("Advanced Options - Main")); + $group->checkbox("show_credits") + ->label(t("Show Site Credits")) + ->checked(module::get_var("gallery", "show_credits")); + $group->checkbox("show_guest_menu") + ->label(t("Show Main Menu for Guest Users")) + ->checked(module::get_var("th_greydragon", "show_guest_menu")); + $group->checkbox("loginmenu_position") + ->label(t("Place Login Link in the Header")) + ->checked(module::get_var("th_greydragon", "loginmenu_position") == "header"); + $group->checkbox("mainmenu_position") + ->label(t("Alternate Header Layout")) + ->checked(module::get_var("th_greydragon", "mainmenu_position") == "top"); + $group->checkbox("hide_breadcrumbs") + ->label(t("Hide Breadcrumbs")) + ->checked(module::get_var("th_greydragon", "hide_breadcrumbs")); + $group->dropdown("photonav_position") + ->label(t("Item Navigator Position")) + ->options(array("top" => t("Top"), "bottom" => t("Bottom"), "both" => t("Both"), "none" => t("None"))) + ->selected(module::get_var("th_greydragon", "photonav_position")); + $group->checkbox("disable_seosupport") + ->label(t("Disallow Search Engine Indexing")) + ->checked(module::get_var("th_greydragon", "disable_seosupport")); + $group->checkbox("enable_pagecache") + ->label(t("Enable Page Cache (60 seconds)")) + ->checked(module::get_var("th_greydragon", "enable_pagecache")); + + $group = $form->group("edit_theme_adv_thumb")->label(t("Advanced Options - Album page/Thumbs")); + $group->dropdown("thumb_ratio") + ->label(t("Aspect Ratio")) + ->options(array("photo" => t("Actual Size"), "digital" => t("Digital 4:3"), "film" => t("Film 3:2") /* , "square" => t("Square 1:1") */ )) + ->selected(module::get_var("th_greydragon", "thumb_ratio")); + $group->dropdown("thumb_descmode") + ->label(t("Title Display Mode")) + ->options(array("overlay" => t("Overlay"), "bottom" => t("Bottom"), "hide" => t("Hide"))) + ->selected(module::get_var("th_greydragon", "thumb_descmode")); + $group->checkbox("hide_thumbmeta") + ->label(t("Hide Item Meta Data")) + ->checked(module::get_var("th_greydragon", "hide_thumbmeta")); + + $group = $form->group("edit_theme_adv_photo")->label(t("Advanced Options - Photo page")); + $group->dropdown("photo_descmode") + ->label(t("Description Display Mode")) + ->options(array("overlay" => t("Overlay"), "bottom" => t("Bottom"), "top" => t("Top"), "hide" => t("Hide"))) + ->selected(module::get_var("th_greydragon", "photo_descmode")); + $group->checkbox("desc_allowbbcode") + ->label(t("Allow BBCode/HTML in Descriptions")) + ->checked(module::get_var("th_greydragon", "desc_allowbbcode")); + $group->checkbox("hide_photometa") + ->label(t("Hide Item Meta Data")) + ->checked(module::get_var("th_greydragon", "hide_photometa", TRUE)); + + $group = $form->group("edit_theme_side")->label(t("Sidebar Options")); + $group->checkbox("hide_blockheader") + ->label(t("Hide Block Header")) + ->checked(module::get_var("th_greydragon", "hide_blockheader")); + $group->checkbox("sidebar_albumonly") + ->label(t("Show Sidebar for Albums Only")) + ->checked(module::get_var("th_greydragon", "sidebar_albumonly")); + $group->dropdown("sidebar_allowed") + ->label(t("Allowed Sidebar Positions")) + ->options(array("any" => t("Any"), "left" => t("Left"), "right" => t("Right"), "none" => t("Default Only"))) + ->selected($sidebar_allowed); + $group->dropdown("sidebar_visible") + ->label(t("Default Sidebar Position")) + ->options(array("right" => t("Right"), "left" => t("Left"), "none" => t("No sidebar"))) + ->selected($sidebar_visible); + + $group = $form->group("maintenance")->label("Maintenance"); + $group->checkbox("build_resize")->label(t("Mark all Image Resizes for Rebuild"))->checked(false); + $group->checkbox("build_thumbs")->label(t("Mark all Thumbnails for Rebuild"))->checked(false); + $group->checkbox("build_exif")->label(t("Reset Exif Info"))->checked(false); + $group->checkbox("reset_theme")->label(t("Reset Theme to a Default State"))->checked(false); module::event("theme_edit_form", $form); - $group = $form->group("buttons"); - $group->submit("")->value(t("Save")); + $form->submit("g-theme-options-save")->value(t("Save Changes")); + return $form; } - public function index() { - $view = new Admin_View("admin.html"); - $view->content = new View("admin_theme_options.html"); - $view->content->form = self::get_edit_form_admin(); - print $view; + protected function get_edit_form_help() { + $help = '
      '; + $help .= 'Help
        '; + $help .= '
      • Prerequisites

        +

        Requirements need to be met for theme to function properly.

        +

        If indicated please download and install + Shadowbox module. Module is required to properly display photos in maximized view and for any admin operations dialogs.

        +
      • '; + + $help .= '
      • Module Recommendations

        +

        Some recommendations to make your experience with the theme more pleasant.

        +

        While there is some support for default Organize module, theme may not skin it properly all the way. + Please consider using GWT Organize Module instead.

        +

        Enable Keyboard navigation by installing Kbd Navigation Module.

        +
      • '; + + $help .= '
      • General Settings

        +

        Theme is designed to display thumbnails in fixed 3+sidebar or 4 columns format. + Number of Rows per Album Page however can be adjusted.
        + Unlike in default theme, thumbnails size is restricted to max 200x200px.

        +

        Default G3 logo can be replaced with your own by providing Alternate Logo Image. + Recommended logo size is within 300x80px. If you need bigger space for your logo, CSS would have to be adjusted.

        +

        Logo could be suppressed altogether by providing Header Text which would take its place. + Footer Text would be simply placed next to Site\'s credits.

        +

        To indicate your rights for the artwork displayed Copyright Message can be placed in + right top corner of the footer.

        +

        Important feature of the theme is ability to specify Selected Color Pack. Color Pack is small CSS + file which defines color rules for the theme. By default theme comes with GreyDragon (default) and Wind sets, + but it could be easily extended. Visit our Download page for additional information.

        +
      • '; + $help .= '
      • Advanced Options

        +

        Show Site Credits simply shows appreciation for hard work of G3 team and Theme\'s author + (you could do also do this by clicking Donate link above).

        +

        If main menu has functionality intended for guest users you can use Show Main Menu for Guest Users + to keep it visible.

        +

        If you do not like login link in the footer you can move it into top right corner by selecting Place Login Link in the Header.

        +

        You can go even further and move main menu to the top of the header with breadcrumbs taking it place by selecting Alternate Header Layout.

        +

        Item Navigator Position could be changed to display it above and/or below the main content.

        +

        Thumb: Aspect Ratio should be used with understanding that some information + may be out of visible area in photo thumbs. Based on specified aspect all thumbs sizes would be adjusted + accordingly. When switching to/from Actual Size, it is recommended to rebuild thumbs for proper display + (see Maintenance section below).

        +

        If you prefer including Item\'s caption as part of the thumb, you can use Thumb: Title Display Mode to change + default Overlay mode. And if metadata (owner/clicks) is not necessary, it could be hidden with Thumb: Hide Item Meta Data.

        +

        Similar to Thumb option above Item\'s description could be displayed where available. + In non-Overlay mode, this is not limited to just Photo page, but description could be + displayed for albums also.

        +
      • '; + $help .= '
      • Sidebar Options

        +

        If Block\'s header is not desired, it could be removed using Hide Block Header.

        +

        Sidebar visibility could be limited to individual Photo pages with + Show Sidebar for Albums Only. +

        When sidebar is visible it can be placed on the left or right of the + screen or removed altogether using Allowed Sidebar Positions. + If more than one position is allowed, Default Sidebar Position + would indicate default state, but visitor would able change it later. +

      • '; + $help .= '
      • Maintenance

        +

        Without changing image size, you can Mark all Resizes for Rebuild. + Then you need to visit Admin\Maintenance to initiate the process. +

        Same can be done for image thumbs with Mark all Thumbnails for Rebuild. +

        Reset Exif Info would remove all exif info allowing it to be imported again.

        +

        And just in case you think that something is not right, you can + always Reset Theme to a Default State. +

      • '; + $help .= '
      '; + return $help; + } + + private function save_item_state($statename, $state, $value) { + if ($state): + module::set_var("th_greydragon", $statename, $value); + else: + module::clear_var("th_greydragon", $statename); + endif; } public function save() { + site_status::clear("gd_init_configuration"); access::verify_csrf(); $form = self::get_edit_form_admin(); - if ($form->validate()) { - $edit_theme = $form->edit_theme; + if ($form->validate()): + module::clear_var("th_greydragon", "photonav_top"); + module::clear_var("th_greydragon", "photonav_bottom"); + module::clear_var("th_greydragon", "hide_sidebar_photo"); + module::clear_var("th_greydragon", "hide_thumbdesc"); + module::clear_var("th_greydragon", "use_detailview"); - module::set_var("gallery", "page_size", $edit_theme->row_count->value * 3); + if ($form->maintenance->reset_theme->value): + module::set_var("gallery", "page_size", 9); + module::set_var("gallery", "resize_size", 640); + module::set_var("gallery", "thumb_size", 200); - $resize_size = $edit_theme->resize_size->value; - $thumb_size = 200; - $build_resize = $edit_theme->build_resize->value; - $build_thumbs = $edit_theme->build_thumbs->value; + module::set_var("gallery", "header_text", ""); + module::set_var("gallery", "footer_text", ""); + module::clear_var("th_greydragon", "copyright"); + module::clear_var("th_greydragon", "logo_path"); + module::clear_var("th_greydragon", "color_pack"); + + module::clear_var("th_greydragon", "enable_pagecache"); + module::set_var("gallery", "show_credits", FALSE); + module::clear_var("th_greydragon", "show_guest_menu"); + module::clear_var("th_greydragon", "mainmenu_position"); + module::clear_var("th_greydragon", "loginmenu_position"); + module::clear_var("th_greydragon", "hide_breadcrumbs"); + module::clear_var("th_greydragon", "horizontal_crop"); + module::clear_var("th_greydragon", "thumb_descmode"); + module::clear_var("th_greydragon", "hide_thumbmeta"); + module::clear_var("th_greydragon", "hide_blockheader"); + module::clear_var("th_greydragon", "photonav_position"); + module::clear_var("th_greydragon", "photo_descmode"); + module::clear_var("th_greydragon", "desc_allowbbcode"); + module::clear_var("th_greydragon", "hide_photometa"); + module::clear_var("th_greydragon", "disable_seosupport"); - if (module::get_var("gallery", "resize_size") != $resize_size) { - module::set_var("gallery", "resize_size", $resize_size); - $build_resize = true; - } - if (module::get_var("gallery", "thumb_size") != $thumb_size) { - module::set_var("gallery", "thumb_size", $thumb_size); - } + module::clear_var("th_greydragon", "sidebar_albumonly"); + module::clear_var("th_greydragon", "sidebar_allowed"); + module::clear_var("th_greydragon", "sidebar_visible"); - if ($build_resize) { - graphics::remove_rule("gallery", "resize", "gallery_graphics::resize"); - graphics::add_rule("gallery", "resize", "gallery_graphics::resize", - array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), 100); - } + module::event("theme_edit_form_completed", $form); + message::success(t("Theme details are reset")); + else: + // * General Settings **************************************************** - if ($build_thumbs) { - graphics::remove_rule("gallery", "thumb", "gallery_graphics::resize"); - graphics::add_rule("gallery", "thumb", "gallery_graphics::resize", - array("width" => 200, "height" => 200, "master" => Image::AUTO), 100); - } + $_priorratio = module::get_var("th_greydragon", "thumb_ratio"); + if (!$_priorratio): + $_priorratio = "digital"; + endif; - module::set_var("th_greydragon", "photonav_top", $edit_theme->photonav_top->value); - module::set_var("th_greydragon", "photonav_bottom", $edit_theme->photonav_bottom->value); + $resize_size = $form->edit_theme->resize_size->value; + $thumb_size = 200; - $sidebar_allowed = $edit_theme->sidebar_allowed->value; - $sidebar_visible = $edit_theme->sidebar_visible->value; + $build_resize = $form->maintenance->build_resize->value; + $build_thumbs = $form->maintenance->build_thumbs->value; + $build_exif = $form->maintenance->build_exif->value; - if ($sidebar_allowed == "none") { $sidebar_visible = "none"; } - if ($sidebar_allowed == "right") { $sidebar_visible = "right"; } - if ($sidebar_allowed == "left") { $sidebar_visible = "left"; } + $thumb_ratio = $form->edit_theme_adv_thumb->thumb_ratio->value; + if ($thumb_ratio == "photo") { $rule = Image::AUTO; } else { $rule = Image::WIDTH; } + $color_pack = $form->edit_theme->colorpack->value; + $thumb_descmode = $form->edit_theme_adv_thumb->thumb_descmode->value; + $photo_descmode = $form->edit_theme_adv_photo->photo_descmode->value; - module::set_var("th_greydragon", "sidebar_allowed", $sidebar_allowed); - module::set_var("th_greydragon", "sidebar_visible", $sidebar_visible); + if ($build_resize): + graphics::remove_rule("gallery", "resize", "gallery_graphics::resize"); + graphics::add_rule("gallery", "resize", "gallery_graphics::resize", + array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), 100); + endif; + if (module::get_var("gallery", "resize_size") != $resize_size): + module::set_var("gallery", "resize_size", $resize_size); + endif; - module::set_var("gallery", "header_text", $edit_theme->header_text->value); - module::set_var("gallery", "footer_text", $edit_theme->footer_text->value); - module::set_var("gallery", "show_credits", $edit_theme->show_credits->value); + if ($build_thumbs): + graphics::remove_rule("gallery", "thumb", "gallery_graphics::resize"); + graphics::add_rule("gallery", "thumb", "gallery_graphics::resize", + array("width" => $thumb_size, "height" => $thumb_size, "master" => $rule), 100); + endif; - module::set_var("th_greydragon", "copyright", $edit_theme->copyright->value); - module::set_var("th_greydragon", "logo_path", $edit_theme->logo_path->value); + if ($build_exif): + db::build() + ->delete("exif_records") + ->execute(); + endif; - module::event("theme_edit_form_completed", $form); + if (module::get_var("gallery", "thumb_size") != $thumb_size): + module::set_var("gallery", "thumb_size", $thumb_size); + endif; + module::set_var("gallery", "header_text", $form->edit_theme->header_text->value); + module::set_var("gallery", "footer_text", $form->edit_theme->footer_text->value); + $this->save_item_state("copyright", $form->edit_theme->copyright->value, $form->edit_theme->copyright->value); + $this->save_item_state("logo_path", $form->edit_theme->logo_path->value, $form->edit_theme->logo_path->value); + $this->save_item_state("color_pack", (($color_pack) and ($color_pack != "greydragon")), $color_pack); + + // * Advanced Options - main ********************************************* + + module::set_var("gallery", "show_credits", $form->edit_theme_adv_main->show_credits->value); + $this->save_item_state("show_guest_menu", $form->edit_theme_adv_main->show_guest_menu->value, TRUE); + $this->save_item_state("loginmenu_position", $form->edit_theme_adv_main->loginmenu_position->value == "1", "header"); + $this->save_item_state("mainmenu_position", $form->edit_theme_adv_main->mainmenu_position->value == "1", "top"); + $this->save_item_state("hide_breadcrumbs", $form->edit_theme_adv_main->hide_breadcrumbs->value, TRUE); + $this->save_item_state("photonav_position", $form->edit_theme_adv_main->photonav_position->value != "top", $form->edit_theme_adv->photonav_position->value); + $this->save_item_state("enable_pagecache", $form->edit_theme_adv_main->enable_pagecache->value, TRUE); + $this->save_item_state("disable_seosupport", $form->edit_theme_adv_main->disable_seosupport->value, TRUE); + + // * Advanced Options - Album page *************************************** + + $this->save_item_state("thumb_ratio", $thumb_ratio != "photo", $thumb_ratio); + $this->save_item_state("thumb_descmode", $thumb_descmode != "overlay", $thumb_descmode); + $this->save_item_state("hide_thumbmeta", $form->edit_theme_adv_thumb->hide_thumbmeta->value, TRUE); + + // * Advanced Options - Photo page *************************************** + + $this->save_item_state("photo_descmode", $photo_descmode != "overlay", $photo_descmode); + $this->save_item_state("desc_allowbbcode", $form->edit_theme_adv_photo->desc_allowbbcode->value, TRUE); + $this->save_item_state("hide_photometa", !$form->edit_theme_adv_photo->hide_photometa->value, FALSE); + + // * Sidebar Options **************************************************** + + $sidebar_allowed = $form->edit_theme_side->sidebar_allowed->value; + $sidebar_visible = $form->edit_theme_side->sidebar_visible->value; + + if ($sidebar_allowed == "right"): + $sidebar_visible = "right"; + endif; + if ($sidebar_allowed == "left"): + $sidebar_visible = "left"; + endif; + + $this->save_item_state("hide_blockheader", $form->edit_theme_side->hide_blockheader->value, TRUE); + $this->save_item_state("sidebar_albumonly", $form->edit_theme_side->sidebar_albumonly->value, TRUE); + $this->save_item_state("sidebar_allowed", $sidebar_allowed != "any", $sidebar_allowed); + $this->save_item_state("sidebar_visible", $sidebar_visible != "right", $sidebar_visible); + + if (($sidebar_allowed == "none") and ($sidebar_visible == "none")): + module::set_var("gallery", "page_size", $form->edit_theme->row_count->value * 4); + else: + module::set_var("gallery", "page_size", $form->edit_theme->row_count->value * 3); + endif; + + module::event("theme_edit_form_completed", $form); + + if ($_priorratio != $thumb_ratio): + message::warning(t("Thumb aspect ratio has been changed. Consider rebuilding thumbs if needed.")); + endif; + + message::success(t("Updated theme details")); + endif; - message::success(t("Updated theme details")); url::redirect("admin/theme_options"); - } else { + else: $view = new Admin_View("admin.html"); $view->content = $form; print $view; - } + endif; + } + + public function index() { + site_status::clear("gd_init_configuration"); + + $view = new Admin_View("admin.html"); + $view->page_title = t("Grey Dragon Theme"); + $view->content = new View("admin_theme_options.html"); + $view->content->name = self::get_theme_name(); + $view->content->version = self::get_theme_version(); + $view->content->form = self::get_edit_form_admin(); + $view->content->help = self::get_edit_form_help(); + print $view; } } - diff --git a/themes/greydragon/admin/views/admin_theme_options.html.php b/themes/greydragon/admin/views/admin_theme_options.html.php new file mode 100644 index 00000000..6c919461 --- /dev/null +++ b/themes/greydragon/admin/views/admin_theme_options.html.php @@ -0,0 +1,59 @@ + + + + +
      +
      +
      -
      + +
      +
      + +
      +
      + +
      +
      diff --git a/themes/greydragon/changelog.txt b/themes/greydragon/changelog.txt index f6750d23..3d5a1451 100644 --- a/themes/greydragon/changelog.txt +++ b/themes/greydragon/changelog.txt @@ -1,46 +1,239 @@ -Grey Dragon Theme Changelog - -version 1.5.8 -- Finally admin module for theme is there. After theme installation, visit Appearance/Theme Options to configure the theme. - If you had older version of the theme, initial setup is also required. - The following settings are available: - - Rows per album page - theme uses 3 columns layout for pictures, therefore default page_size is computed in x3 increments - - Thumb size is restricted to 200 and therefore not available for administration - - Mark to build resizes/thumbs - allows force rebuilding of images - - Show/Hide top/bottom photo navigators - - Specify allowed and default sidebar position - - Administrator can now specify Copyright message to display in the footer - - Site logo is now default to Gallery 3 logo, but admin can provide a path to custom logo. -- Sidebar session cookie is set to expire in 365 days - -version 1.5.7 -- Status message has been moved into header as popup to prevent obstruction of the main view. - jQuery is used to fade it out in 10 sec. -- Improved logic for dialogs on submit -- Theme related JS has been moved out of the page.html.php - -version 1.5.6 -- Fixed issue with tollbar buttons not properly aligned/shown when page is resized. -- Copyright info moved into DB. To change default settings add [th_greydragon/copyright] into VARS table. - -version 1.5.5 -- CSS fixes. -- Theme adjusted to be compatible with latest Git. -- Login links are moved into footer. -- Pagination module redesigned to support new structure of paging data. - -version 1.5.4 -- CSS fixes. -- Added support for Comments block. -- Improved support for Modal dialogs. - -version 1.5.3 -- Sync to git. -- Exif menu customization is now part of the theme. -- Sidebar management button is disabled for current mode. - -version 1.5.2 -- Code, layout, css cleanup. -- New thumbs for buttons. -- First set of Ajax dialogs is ready and now operational: Login, user info, edit album, exit info. +=== Grey Dragon Theme === +Grey Dragon Theme - a custom theme for Gallery 3 + +This theme was designed and built by Serguei Dosyukov, whose blog you will find at http://blog.dragonsoft.us/ +Copyright (C) 2009-2010 Serguei Dosyukov + +Tested up to: G3 3.0 RC2 (Santa Fe) Experimental +Minimum requirement: G3 3.0 RC2 (Santa Fe) Experimental +Donate link: http://blog.dragonsoft.us/gallery-3/ + +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. + +=== Open issues === +- Issue with Delete functionality +- Support for new organize module +- Support for Register module +- Issue with Comments module + +=== Changelog === + +version 2.3.1 +- Hide Rotate operations for pictures since they are not supported by the theme +- Added use of common gallery.ajax.js. Fix issue with some Ajax based links. +- Layout fixes for Translation form overlay +- Changed CSS styling for buttons to provide unified coverage for buttons and links exposed as buttons. +- ADMIN: Fixed options group styles in Theme's Admin panel +- ADMIN: Advanced Settings for Thumbs and Individual Photo are moved into separate sections. +- ADMIN: New option - display meta data in Photo description section +- ADMIN: New option/fix - SEO indexing is now allowed by default. In order to prevent your site from being indexed, you can now use "Disallow Search Engine Indexing" option + +version 2.3.0 +- Adopted for Gallery 3.0RC2 changes (minor template adjustments, css class name changes, etc.) + +version 2.2.1 +- Redesigned Ready event handler for the theme to ensure proper ShadowBox initialization +- Added support for gallery_dialog() function call used by some 3rd party modules - some sync issues are solved by imposed delay of 1 second +- GPS module - better action list alignment in the sidebar + +version 2.2.0 +- Added support for slideshow mode in Photo Preview +- Fixed issue with Info side block - missing markup +- Fixed issue with Upload dialog layout with some resolutions/fonts +- ADMIN: Added option to hide breadcrumbs +- ADMIN: Added prerequisite check for Info module - required for Thumb meta data display + +version 2.1.7 +- Added support for missing images in the thumbs to allow proper operations with empty albums or albums with broken thumbs +- Some color optimizations +- Color improvements for "Add Image" dialog +- Better support for Basket module + +version 2.1.6 +- Wind colorpack adjusted to closer match default Wind theme + +version 2.1.5 +- Minor changes in ADMIN infrastructure +- ADMIN: added check for Kbd Navigation module +- ADMIN: New color pack - carbon + +version 2.1.4 +- Minor refactoring in paginator +- Added support for keyboard navigation module (http://codex.gallery2.org/Gallery3:Modules:kbd_nav) + +version 2.1.3 +- Sidebar restricted to item related pages (album, photo, movie, etc) +- Fixed issue with bottom border not applied to all instances of H1 tag +- Min footer size set to 4em +- ADMIN: "Photo: Description Display Mode" option added +- ADMIN: Added new maintenance operation - "Reset Exif Info" + +version 2.1.2 +- Fixed issue with Album thumbs - empty space under +- Thumb Item's Title Display Mode expanded to be applied to Item's description in Photo page +- More documentations in CSS files, some movements +- Some cleanup for Wind color pack +- Fixed font name typo in screen.css +- Fixed "Waiting" roller for Wind theme to match background +- Added "up" button in navigation + +version 2.1.1 +- Increased size of Add photo dialog for better display on some lower resolutions. +- ADMIN: New option: "Thumb: Item's Title Display Mode" - specifies how to display item's title in thumbs : Overlay Bottom Hide + +version 2.1.0 +- Custom Info Block to include item's description +- Image is centered when "Actual Size" aspect is used for thumbs +- Added support for color packs - included: greydragon, wind +- ADMIN: Improved error handling +- ADMIN: Disable submit button on click to prevent Dbl-click +- ADMIN: New option: Enable page cache - adds header marker for page to be cached for 60 seconds + +version 2.0.1 +- Enable BBCode/HTML support in individual photo descriptions +- Fixed main menu overlay issue when in top position +- Theme's credits moved into dedicated method +- CSS clean up +- Comments module layout enhancements + +version 2.0.0 +- Major redesign of the gallery flow. + - Added caption and metadata (Admin/optional) overlay for thumbs. + - Added description overlay in individual Photo view (look for "Learn More" marker). + - Based on Admin setting, thumbs are adjusted to fit Digital/Film/Actual size. +- Attempt to fix issue with JS load latency to prevent unhandled AJAX calls +- Added code protection for theme initialization procedure +- ADMIN: Thumb Aspect Ratio option. See help section for more info. + +version 1.8.2 +- Increased based font size +- Layout adjusted to match new settings +- ADMIN: New option - Place Login Link in the Header + +version 1.8.1 +- ADMIN: small adjustments in layout and help info +- 3rd party module's related CSS moved into contrib.css +- Adjust user profile screen to match new layout +- initial design for calendar module + +version 1.8.0 +- ADMIN: Major redesign of the layout. Help section added. +- ADMIN: New option - Show main menu for guest user +- Minimum required Gallery version set to 30 +- When configured not to use sidebar, theme is switched into 4 columns layout + +version 1.7.6 +- Organize module: CSS improvements +- Fixed issue with Chrome browser + +version 1.7.5 +- ADMIN: Added option to reset theme to default state +- CSS: some size adjustments for dialogs. Added minimum height for overlay to keep dialogs from shrinking. + +version 1.7.4 +- ADMIN: Theme Gallery 3 core requirement changed to v.26 +- ADMIN: Most of theme's settings are documented using element's title attribute - hover over to see a description +- Edit Permissions form redesigned and enlarged to fit more information + +version 1.7.3 +- ADMIN: Default states for the theme options are no longer being stored. Please save theme settings at least once to take advantage of a new functionality. +- Photo Navigator default position is set to Top Only + +version 1.7.2 +- Fix in Uploader dialog to keep items inside respected boxes +- Organize module support has been abandoned. Please use GWT Organize module instead. Added item in Prerequisites Checklist. + +version 1.7.1 +- CSS: Fixed visibility of the "Select Photo" button in "Add photo" dialog +- CSS: Fixed "ghost" line for navigation buttons when zoomed-in in IE +- Admin: fixed issue with prerequisite check not detecting deleted modules +- /views/support folder deprecated. Logic moved into Theme_View extension class for Theme_View_Core +- Theme Options Page management, generic Page code and BBCode processor moved into Theme_View class +- HACK: Info block is not displayed if there is no description for the item + +version 1.6.4 +- Admin: Added "Show Sidebar for Albums only" option +- Admin: added error visibility to the requirements validation list +- Small CSS adjustments: Fixed footer min size issue when no site credit info is displayed; added space between Credits in the footer and Footer text area. +- Few missing parts from last git sync + +version 1.6.3 +- Kohana 2.4 support +- Support for Movie files view +- Admin: Allow hide Sidebar Block header + +version 1.6.2 +- Admin: Page navigator option changed to use combobox +- Admin: Added option to hide item description in albums + +version 1.6.2 +- Small CSS adjustments. +- All operation dialogs should be visible now +- Context menu: "Rotate 90..." items are removed due to an issue with image quality affected by the operation +- Context menu: "Choose as the album cover" is now properly handled + +version 1.6.1 +- Admin: When allowed sidebar position is "Default Only", don't disregard selected Default position +- Adjust item's toolbar buttons to align properly when side bar position is fixed +- BBCode parser improved to support stripping of BBCode for Page title and breadcrumbs +- Fixed issue with main menu missing class declaration not allowing open dialogs +- Adjust context dialogs to properly show caption info +- Caption added to Full size Preview +- "New Comment" form styled +- Admin: Option to align main menu to the top and Breadcrumbs to the left + +version 1.6.0 +- Admin: Fixed issue with "Rebuild thumbs" option in theme admin +- Admin: Fixed issue with Item's toolbar not properly aligned in Quirks Mode +- Exif data dialog Layout changes +- Item context menu improvements: + - Fixed issue with submit logic + - Layout fixes for context menu dialogs + +version 1.5.8 +- Admin: First release of the Theme admin option. After theme installation, visit Appearance/Theme + Options to configure the theme. If you had older version of the theme, initial setup is also required. + The following settings are available: + - Rows per album page - theme uses 3 columns layout for pictures, therefore default page_size is computed in x3 increments + - Thumb size is restricted to 200 and therefore not available for administration + - Mark to build resizes/thumbs - allows force rebuilding of images + - Show/Hide top/bottom photo navigators + - Specify allowed and default sidebar position + - Administrator can now specify Copyright message to display in the footer + - Site logo is now default to Gallery 3 logo, but admin can provide a path to custom logo. + - Admin module validates Theme's requirements (Shadowbox module need to be installed/active) +- Sidebar session cookie is set to expire in 365 days + +version 1.5.7 +- Status message has been moved into header as popup to prevent obstruction of the main view. + jQuery is used to fade it out in 10 sec. +- Improved logic for dialogs on submit +- Theme related JS has been moved out of the page.html.php + +version 1.5.6 +- Fixed issue with tollbar buttons not properly aligned/shown when page is resized. +- Copyright info moved into DB. To change default settings add [th_greydragon/copyright] into VARS table. + +version 1.5.5 +- CSS fixes. +- Theme adjusted to be compatible with latest Git. +- Login links are moved into footer. +- Pagination module redesigned to support new structure of paging data. + +version 1.5.4 +- CSS fixes. +- Added support for Comments block. +- Improved support for Modal dialogs. + +version 1.5.3 +- Updated to match latest git. +- Exif menu customization is now part of the theme. +- Sidebar management button is disabled for current mode. + +version 1.5.2 +- Code, layout, css cleanup. +- New thumbs for buttons. +- First set of Ajax dialogs is ready and now operational: Login, user info, edit album, exit info. - Fixed some browser related issues. \ No newline at end of file diff --git a/themes/greydragon/controllers/greydragon.php b/themes/greydragon/controllers/greydragon.php new file mode 100644 index 00000000..0796d801 --- /dev/null +++ b/themes/greydragon/controllers/greydragon.php @@ -0,0 +1,39 @@ +page_title = t("%name Profile", array("name" => $user->display_name())); + $v->content = new View("user_profile.html"); + + $v->content->user = $user; + $v->content->contactable = + !$user->guest && $user->id != identity::active_user()->id && $user->email; + $v->content->editable = + identity::is_writable() && !$user->guest && $user->id == identity::active_user()->id; + + $event_data = (object)array("user" => $user, "content" => array()); + module::event("show_user_profile", $event_data); + $v->content->info_parts = $event_data->content; + + print $v; + } +*/ +} diff --git a/themes/greydragon/css/colorpacks/carbon/colors.css b/themes/greydragon/css/colorpacks/carbon/colors.css new file mode 100644 index 00000000..57fd30dd --- /dev/null +++ b/themes/greydragon/css/colorpacks/carbon/colors.css @@ -0,0 +1,192 @@ +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * ColorPack: Carbon - Default color pack + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* styles.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +html { background-color: #333; } +body { color: #999; background-color: #333; } + +h1 { border-bottom: #6f6f6f 1px solid; } +a { color: #999 !important; font-weight: bold; } +.ui-icon { background-image: url(images/ui-icons.png); } + +/* styles.css - Header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-header .g-message-block { border: 1px #888 solid; background-color: #AAA; color: #000; } +.g-breadcrumbs li { background: transparent url(images/ico-separator.png) no-repeat 0 0.2em; } + +/* styles.css - Content ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-main { background-color: #3f3f3f; margin-left: 10px; margin-right: 10px; } + +/* styles.css - Footer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +/* styles.css - Album Layout ~~~~~~~~~~~~~~~~~~~~~~~~~*/ +#g-info .g-description { border: #6f6f6f 1px solid; } + +.g-thumbslide, .g-thumbslide-ext { border: 1px solid #303E43; background-color: #555; } +.g-thumbcrop { border: 1px solid #303E43; } + +.g-album .g-thumbslide, +.g-album .g-thumbslide-ext { border-top: 1px solid #6f6f6f; border-left: 1px solid #6f6f6f; border-right: 4px double #6f6f6f; border-bottom: 4px double #6f6f6f; } +.g-photo .g-thumbslide, /* Need to compensate for double border in album's thumbs */ +.g-photo .g-thumbslide-ext { margin-bottom: 3px; } + +.g-thumbslide:hover .g-description { color: #fff; border-bottom: 1px solid #999; background: #1E1E1E; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } +.g-album .g-thumbslide:hover .g-description, +.g-album .g-thumbslide-ext .g-description { background: #555 url(images/ico-album.png) no-repeat 4px 2px; } + +.g-thumbslide:hover .g-metadata, +.g-thumbslide-ext:hover .g-metadata { border-top: 1px solid #999; background: #1E1E1E; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } + +/* styles.css - Photo Layout ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +div.g-resize { border: 1px solid #888; background: #555; } + +div.g-resize:hover .g-description { color: #fff; background: #1E1E1E; border-bottom: 1px solid #999; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } +div.g-resize .g-more { border: 1px solid #999; background: #1E1E1E; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } + +.g-movie { border: 1px solid #888; padding: 5px; background: #555; } + +/* styles.css - Reauthentificate ~~~~~~~~~~~~~~~~~~~~~*/ + +#g-reauthenticate-form ul { border: 1px #888 solid; } + +/* styles.css - Sidebar Blocks ~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-toolbar { border-bottom: 1px solid #737373; } + +/* styles.css - Sidebar Blocks : Common ~~~~~~~~~~~~~~*/ + +.g-block { border: 1px solid #737373; } +.g-block h2 { background: url(images/section.png) repeat-x; } + +/* styles.css - Sidebar Blocks : Buttons ~~~~~~~~~~~~~*/ + +#g-viewformat .g-viewthumb-left { background: url('images/view-left.png') no-repeat left top; } +#g-viewformat .g-viewthumb-right { background: url('images/view-right.png') no-repeat left top; } +#g-viewformat .g-viewthumb-full { background: url('images/view-full.png') no-repeat left top; } + +#g-slideshow-link { background: url("images/view-slideshow.png") top left no-repeat; } +.g-fullsize-link { background: url("images/view-fullsize.png") top left no-repeat; } +#g-exifdata-link { background: url("images/view-info.png") top left no-repeat; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* menus.css ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-site-menu ul { border: #000000 0 solid; } +#g-site-menu li a:hover { color: #000000; background-color: #333; } +#g-site-menu li:hover, +#g-site-menu li.iemhover { border: #303030 1px solid; background-color: #333; border-bottom: #000000 1px solid; } +#g-site-menu li ul { border: #000000 1px solid; } +#g-site-menu li ul li { border: #C0C0C0 0px solid; background-color: #333; } +#g-site-menu li ul li:hover, +#g-site-menu li ul li.iemhover { border: #C0C0C0 0 solid; background-color: #ddf2ff; } + +.g-item .g-context-menu { background-image: url(images/ui-icons.png); } +.g-item .g-context-menu:hover { background: #333 none; border: 1px #888 solid; } +.g-item .g-context-menu li li a:hover { background-color: #ddf2ff; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* forms.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-body { background: #101415 url('images/ajax-loading.gif') no-repeat center center; } +#sb-title { border-left: #303030 1px solid; border-right: #303030 1px solid; background-color: #333; } + +#sb-content.html_ajax p.g-error { color: red; } +#sb-content.html_ajax form { background-color: #101415; } +#sb-content.html_ajax>div { background-color: #101415; } + +/* forms.css - Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-permissions .g-breadcrumbs { border: #303030 1px solid; } +#sb-content #g-edit-permissions-form { border: #303030 1px solid; } +#sb-content #g-move>ul { border: #303030 1px solid; } + +/* forms.css - Add item ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-add-photos-form .g-breadcrumbs { border: #303030 1px solid; } + +#g-add-photos-canvas { background-color: #101010; border: #303030 1px solid; } +#g-add-photos-button { border: #303030 1px solid; color: #bbb; } +#g-add-photos-status { background-color: #101010; border: #303030 1px solid; } + +#g-add-photos-status li.g-success { background: #d9efc2 url('images/ico-success.png') no-repeat .4em 50%; } +#g-add-photos-status li.g-error { background: #f6cbca url('images/ico-error.png') no-repeat .4em 50%; color: #f00; } + +/* forms.css - Organize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content.html_ajax #g-organize { border: #303030 1px solid; } + +#g-organize-detail { border-left: #303030 1px solid; } +#g-organize .g-message-block { border-bottom: #303030 1px solid; } +.g-organize-microthumb-grid-cell { background-color: #303030; } +.g-organize-microthumb { background-color: #707070; } +#g-organize-controls { border-top: #303030 1px solid; } + +/* forms.css - User Profile ~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-user-profile .g-avatar { border: 1px solid #888; background: #555; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* menus.css ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-site-menu ul { border: #000000 0 solid; } +#g-site-menu li a:hover { color: #000000; background-color: #303030; } +#g-site-menu li:hover, +#g-site-menu li.iemhover { border: #303030 1px solid; background-color: #303030; border-bottom: #000000 1px solid; } +#g-site-menu li ul { border: #000000 1px solid; } +#g-site-menu li ul li { border: #C0C0C0 0px solid; background-color: #212121; } +#g-site-menu li ul li:hover, +#g-site-menu li ul li.iemhover { border: #C0C0C0 0 solid; background-color: #303030; } + +.g-item .g-context-menu { background-image: url(images/ui-icons.png); } +.g-item .g-context-menu:hover { background: #181818 none; border: 1px #888 solid; } +.g-item .g-context-menu li li a:hover { background-color: #303030; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* modules.css - Exif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-exif-data table { border: #303030 1px solid; } +#sb-content #g-exif-data .g-even { background-color: #404040; } +#sb-content #g-exif-data .g-odd { background-color: #303030; } + +/* modules.css - Info module ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-metadata .g-description { border-top: 1px solid #737373; } + +/* modules.css - Image block ~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-image-block img { border: 1px solid #888; background: #555; } + +/* modules.css - Comments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-comments .g-author { border-bottom: 1px solid #202628; color: #999; } +#g-comments-link { background-image: url(images/view-comments.png); } +#g-comment-detail>ul>li { border: 1px dotted #737373; } +#g-comment-form { border: 1px dotted #737373; } + +/* modules.css - Calendar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-view-menu #g-calendarview-link { background-image: url(images/view-calendar.png); } +#g-view-calendar-form ul { border: 1px #888 solid; } +table.calendar { border: #a2adbc 1px solid; color: #616b76; } +table.calendar th { border-bottom: #a2adbc 1px solid; border-right: #a2adbc 1px solid; background: #d9e2e1; color: #616b76; } +table.calendar td { border-bottom: #a2adbc 1px solid; border-right: #a2adbc 1px solid; } +table.calendar td.title { background-color: #a2adbc; color: #fff; } +table.calendar td.title a { color: #fff !important; } +table.calendar td a { color: red !important; } + +/* modules.css - Search ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-quick-search-form input[type="text"] { background-color: transparent; border: 1px solid #737373; color: #BBB; } +#g-quick-search-form input[type="submit"] { background: transparent url(images/search.png) no-repeat center top; border: none; } + +/* modules.css - Basket ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#checkout legend { background: url(images/section.png) repeat-x; } \ No newline at end of file diff --git a/themes/greydragon/images/ajax-loading.gif b/themes/greydragon/css/colorpacks/carbon/images/ajax-loading.gif similarity index 100% rename from themes/greydragon/images/ajax-loading.gif rename to themes/greydragon/css/colorpacks/carbon/images/ajax-loading.gif diff --git a/themes/greydragon/css/colorpacks/carbon/images/ico-album.png b/themes/greydragon/css/colorpacks/carbon/images/ico-album.png new file mode 100644 index 00000000..ac87ec4f Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/ico-album.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/ico-error.png b/themes/greydragon/css/colorpacks/carbon/images/ico-error.png new file mode 100644 index 00000000..c37bd062 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/ico-error.png differ diff --git a/themes/greydragon/images/ico-separator.png b/themes/greydragon/css/colorpacks/carbon/images/ico-separator.png similarity index 100% rename from themes/greydragon/images/ico-separator.png rename to themes/greydragon/css/colorpacks/carbon/images/ico-separator.png diff --git a/themes/greydragon/css/colorpacks/carbon/images/ico-success.png b/themes/greydragon/css/colorpacks/carbon/images/ico-success.png new file mode 100644 index 00000000..a9925a06 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/ico-success.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/search.png b/themes/greydragon/css/colorpacks/carbon/images/search.png new file mode 100644 index 00000000..2d115cc8 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/search.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/section.png b/themes/greydragon/css/colorpacks/carbon/images/section.png new file mode 100644 index 00000000..8180ecb3 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/section.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png b/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png new file mode 100644 index 00000000..7d1723bf Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png b/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png new file mode 100644 index 00000000..5442fa51 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-comments.png b/themes/greydragon/css/colorpacks/carbon/images/view-comments.png new file mode 100644 index 00000000..5449126b Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-comments.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-full.png b/themes/greydragon/css/colorpacks/carbon/images/view-full.png new file mode 100644 index 00000000..7145fd9d Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-full.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png b/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png new file mode 100644 index 00000000..ebd04237 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-info.png b/themes/greydragon/css/colorpacks/carbon/images/view-info.png new file mode 100644 index 00000000..ff30501c Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-info.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-left.png b/themes/greydragon/css/colorpacks/carbon/images/view-left.png new file mode 100644 index 00000000..c59af5d0 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-left.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-right.png b/themes/greydragon/css/colorpacks/carbon/images/view-right.png new file mode 100644 index 00000000..59505456 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-right.png differ diff --git a/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png b/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png new file mode 100644 index 00000000..2fb53ad0 Binary files /dev/null and b/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/colors.css b/themes/greydragon/css/colorpacks/greydragon/colors.css new file mode 100644 index 00000000..22a6d40e --- /dev/null +++ b/themes/greydragon/css/colorpacks/greydragon/colors.css @@ -0,0 +1,178 @@ +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * ColorPack: GreyDragon - Default color pack + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* styles.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +html { background-color: #1A2022; } +body { color: #BBB; background: url(images/background.gif) #1A2022 repeat-x; } + +h1 { border-bottom: #737373 1px solid; } +a { color: #6392CF !important; } +.ui-icon { background-image: url(images/ui-icons.png); } + +/* styles.css - Header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-header .g-message-block { border: 1px #888 solid; background-color: #AAA; color: #000; } +.g-breadcrumbs li { background: transparent url(images/ico-separator.png) no-repeat 0 0.2em; } + +/* styles.css - Footer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-footer { background: url(images/footer.png) #1A2022 repeat-x top !important; } + +/* styles.css - Album Layout ~~~~~~~~~~~~~~~~~~~~~~~~~*/ +#g-info .g-description { border: #737373 1px solid; } + +.g-thumbslide, .g-thumbslide-ext { border: 1px solid #303E43; background: #1E1E1E url('images/image-thumb.gif') repeat-x; } +.g-thumbcrop { border: 1px solid #303E43; } + +.g-album .g-thumbslide, +.g-album .g-thumbslide-ext { border-top: 1px solid #43565B; border-left: 1px solid #43565B; border-right: 4px double #43565B; border-bottom: 4px double #43565B; } +.g-photo .g-thumbslide, /* Need to compensate for double border in album's thumbs */ +.g-photo .g-thumbslide-ext { margin-bottom: 3px; } + +.g-thumbslide:hover .g-description { color: #fff; border-bottom: 1px solid #999; background: #1E1E1E; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } +.g-album .g-thumbslide:hover .g-description, +.g-album .g-thumbslide-ext .g-description { background: #1E1E1E url(images/ico-album.png) no-repeat 4px 2px; } + +.g-thumbslide:hover .g-metadata, +.g-thumbslide-ext:hover .g-metadata { border-top: 1px solid #999; background: #1E1E1E; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } + +/* styles.css - Photo Layout ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +div.g-resize { border: 1px solid #888; background: #555; } + +div.g-resize:hover .g-description { color: #fff; background: #1E1E1E; border-bottom: 1px solid #999; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } +div.g-resize .g-more { border: 1px solid #999; background: #1E1E1E; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } + +.g-movie { border: 1px solid #888; padding: 5px; background: #555; } + +/* styles.css - Reauthentificate ~~~~~~~~~~~~~~~~~~~~~*/ + +#g-reauthenticate-form ul { border: 1px #888 solid; } + +/* styles.css - Sidebar Blocks ~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-toolbar { border-bottom: 1px solid #737373; } + +/* styles.css - Sidebar Blocks : Common ~~~~~~~~~~~~~~*/ + +.g-block { border: 1px solid #737373; background-color: #101415; } +.g-block h2 { background: url(images/section.png) repeat-x; } + +/* styles.css - Sidebar Blocks : Buttons ~~~~~~~~~~~~~*/ + +#g-viewformat .g-viewthumb-left { background: url('images/view-left.png') no-repeat left top; } +#g-viewformat .g-viewthumb-right { background: url('images/view-right.png') no-repeat left top; } +#g-viewformat .g-viewthumb-full { background: url('images/view-full.png') no-repeat left top; } + +#g-slideshow-link { background: url("images/view-slideshow.png") top left no-repeat; } +.g-fullsize-link { background: url("images/view-fullsize.png") top left no-repeat; } +#g-exifdata-link { background: url("images/view-info.png") top left no-repeat; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* forms.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-body { background: #101415 url('images/ajax-loading.gif') no-repeat center center; } +#sb-title { border-left: #303030 1px solid; border-right: #303030 1px solid; background: #101415 url('images/section.png') repeat-x; } + +#sb-content.html_ajax p.g-error { color: red; } +#sb-content.html_ajax form { background-color: #101415; } +#sb-content.html_ajax>div { background-color: #101415; } + +/* styles.css - Photo Slideshow ~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-counter a { color: #fff !important; font-weight: bold; font-size: 11px; } + +/* forms.css - Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-permissions .g-breadcrumbs { border: #303030 1px solid; } +#sb-content #g-edit-permissions-form { border: #303030 1px solid; } +#sb-content #g-move>ul { border: #303030 1px solid; } + +/* forms.css - Add item ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-add-photos-form .g-breadcrumbs { border: #303030 1px solid; } + +#g-add-photos-canvas { background-color: #101010; border: #303030 1px solid; } +#ag-add-photos-button { border: #303030 1px solid; color: #bbb; } +#g-add-photos-status { background-color: #101010; border: #303030 1px solid; } + +#g-add-photos-status li.g-success { background: url('images/ico-success.png') transparent no-repeat .4em 50%; } +#g-add-photos-status li.g-error { background: url('images/ico-error.png') transparent no-repeat .4em 50%; color: #f00; } + +/* forms.css - Organize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content.html_ajax #g-organize { border: #303030 1px solid; } + +#g-organize-detail { border-left: #303030 1px solid; } +#g-organize .g-message-block { border-bottom: #303030 1px solid; } +.g-organize-microthumb-grid-cell { background-color: #303030; } +.g-organize-microthumb { background-color: #707070; } +#g-organize-controls { border-top: #303030 1px solid; } + +/* forms.css - User Profile ~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-user-profile .g-avatar { border: 1px solid #888; background: #555; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* menus.css ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-site-menu ul { border: #000000 0 solid; } +#g-site-menu li a:hover { color: #000000; background-color: #303030; } +#g-site-menu li:hover, +#g-site-menu li.iemhover { border: #303030 1px solid; background-color: #303030; border-bottom: #000000 1px solid; } +#g-site-menu li ul { border: #000000 1px solid; } +#g-site-menu li ul li { border: #C0C0C0 0px solid; background-color: #212121; } +#g-site-menu li ul li:hover, +#g-site-menu li ul li.iemhover { border: #C0C0C0 0 solid; background-color: #303030; } + +.g-item .g-context-menu { background-image: url(images/ui-icons.png); } +.g-item .g-context-menu:hover { background: #181818 none; border: 1px #888 solid; } +.g-item .g-context-menu li li a:hover { background-color: #303030; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* modules.css - Exif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-exif-data table { border: #303030 1px solid; } +#sb-content #g-exif-data .g-even { background-color: #404040; } +#sb-content #g-exif-data .g-odd { background-color: #303030; } + +/* modules.css - Info module ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-metadata .g-description { border-top: 1px solid #737373; } + +/* modules.css - Image block ~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-image-block img { border: 1px solid #888; background: #555; } + +/* modules.css - Comments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-comments .g-author { border-bottom: 1px solid #202628; color: #999; } +#g-comments-link { background-image: url(images/view-comments.png); } +#g-comment-detail>ul>li { border: 1px dotted #737373; } +#g-comment-form { border: 1px dotted #737373; } + +/* modules.css - Calendar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-view-menu #g-calendarview-link { background-image: url(images/view-calendar.png); } +#g-view-calendar-form ul { border: 1px #888 solid; } +table.calendar { border: #a2adbc 1px solid; color: #616b76; } +table.calendar th { border-bottom: #a2adbc 1px solid; border-right: #a2adbc 1px solid; background: #d9e2e1; color: #616b76; } +table.calendar td { border-bottom: #a2adbc 1px solid; border-right: #a2adbc 1px solid; } +table.calendar td.title { background-color: #a2adbc; color: #fff; } +table.calendar td.title a { color: #fff !important; } +table.calendar td a { color: red !important; } + +/* modules.css - Search ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-quick-search-form input[type="text"] { background-color: transparent; border: 1px solid #737373; color: #BBB; } +#g-quick-search-form input[type="submit"] { background: transparent url(images/search.png) no-repeat center top; border: none; } + +/* modules.css - Basket ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#checkout legend { background: url(images/section.png) repeat-x; } diff --git a/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif b/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif new file mode 100644 index 00000000..0996045a Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif differ diff --git a/themes/greydragon/images/background.gif b/themes/greydragon/css/colorpacks/greydragon/images/background.gif similarity index 100% rename from themes/greydragon/images/background.gif rename to themes/greydragon/css/colorpacks/greydragon/images/background.gif diff --git a/themes/greydragon/images/footer.png b/themes/greydragon/css/colorpacks/greydragon/images/footer.png similarity index 100% rename from themes/greydragon/images/footer.png rename to themes/greydragon/css/colorpacks/greydragon/images/footer.png diff --git a/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png b/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png new file mode 100644 index 00000000..ac87ec4f Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png b/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png new file mode 100644 index 00000000..c37bd062 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png b/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png new file mode 100644 index 00000000..3e158515 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png b/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png new file mode 100644 index 00000000..a9925a06 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png differ diff --git a/themes/greydragon/images/main.gif b/themes/greydragon/css/colorpacks/greydragon/images/image-thumb.gif similarity index 55% rename from themes/greydragon/images/main.gif rename to themes/greydragon/css/colorpacks/greydragon/images/image-thumb.gif index 49e2f6c4..bc3a192f 100644 Binary files a/themes/greydragon/images/main.gif and b/themes/greydragon/css/colorpacks/greydragon/images/image-thumb.gif differ diff --git a/themes/greydragon/images/search.png b/themes/greydragon/css/colorpacks/greydragon/images/search.png similarity index 100% rename from themes/greydragon/images/search.png rename to themes/greydragon/css/colorpacks/greydragon/images/search.png diff --git a/themes/greydragon/css/colorpacks/greydragon/images/section.png b/themes/greydragon/css/colorpacks/greydragon/images/section.png new file mode 100644 index 00000000..8180ecb3 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/section.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png b/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png new file mode 100644 index 00000000..7ab15cae Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png b/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png new file mode 100644 index 00000000..206ccd66 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png differ diff --git a/themes/greydragon/images/view-comments.png b/themes/greydragon/css/colorpacks/greydragon/images/view-comments.png similarity index 100% rename from themes/greydragon/images/view-comments.png rename to themes/greydragon/css/colorpacks/greydragon/images/view-comments.png diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-full.png b/themes/greydragon/css/colorpacks/greydragon/images/view-full.png new file mode 100644 index 00000000..b75de946 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-full.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png b/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png new file mode 100644 index 00000000..ed76257a Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-info.png b/themes/greydragon/css/colorpacks/greydragon/images/view-info.png new file mode 100644 index 00000000..521439ce Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-info.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-left.png b/themes/greydragon/css/colorpacks/greydragon/images/view-left.png new file mode 100644 index 00000000..b5b93c7a Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-left.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-right.png b/themes/greydragon/css/colorpacks/greydragon/images/view-right.png new file mode 100644 index 00000000..a400c638 Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-right.png differ diff --git a/themes/greydragon/css/colorpacks/greydragon/images/view-slideshow.png b/themes/greydragon/css/colorpacks/greydragon/images/view-slideshow.png new file mode 100644 index 00000000..c6a471ac Binary files /dev/null and b/themes/greydragon/css/colorpacks/greydragon/images/view-slideshow.png differ diff --git a/themes/greydragon/css/colorpacks/wind/colors.css b/themes/greydragon/css/colorpacks/wind/colors.css new file mode 100644 index 00000000..09ab5a6a --- /dev/null +++ b/themes/greydragon/css/colorpacks/wind/colors.css @@ -0,0 +1,184 @@ +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * ColorPack: Wind - Wind theme-like color pack + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* styles.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +html { background-color: #ccc; } +body { color: #000; background-color: #ccc; padding-left: 10px; padding-right: 10px; } + +a { color: #33629f !important } +.ui-icon { background-image: url(images/ui-icons.png); } + +/* styles.css - Header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-header { background-color: #e8e8e8; border-bottom: #ccc 1px solid; } +#g-header .g-message-block { border: 1px #888 solid; background-color: #aaa; color: #000; } +.g-breadcrumbs li { background: transparent url(images/ico-separator.png) no-repeat 0 0.2em; } + +/* styles.css - Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-main { background-color: #fff; } + +/* styles.css - Footer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-footer { background-color: #e8e8e8; border-top: #ccc 1px solid; } + +/* styles.css - Album Layout ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-info h1, #g-album-header h1 { border-bottom: #ccc 1px solid; } +#g-info .g-description { border: #888 1px solid; } + +.g-thumbslide { border: 1px solid #707E83; background-color: #e8e8e8; } +.g-thumbcrop { border: 1px solid #707E83; } + +.g-album .g-thumbslide, +.g-album .g-thumbslide-ext { border-top: 1px solid #707E83; border-left: 1px solid #707E83; border-right: 4px double #707E83; border-bottom: 4px double #707E83; } +.g-photo .g-thumbslide, /* Need to compensate for double border in album's thumbs */ +.g-photo .g-thumbslide-ext { margin-bottom: 3px; } + +.g-thumbslide:hover .g-description { color: #000; border-bottom: 1px solid #999; background: #e8e8e8; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } +.g-album .g-thumbslide:hover .g-description, +.g-album .g-thumbslide-ext .g-description { background: #fff url(images/ico-album.png) no-repeat 4px 2px; } + +.g-thumbslide:hover .g-metadata, +.g-thumbslide-ext:hover .g-metadata { border-top: 1px solid #999; background: #e8e8e8; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } + +/* styles.css - Photo Layout ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +div.g-resize { border: 1px solid #888; background: #e8e8e8; } + +div.g-resize:hover .g-description { color: #000; background: #e8e8e8; border-bottom: 1px solid #999; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } +div.g-resize .g-more { border: 1px solid #999; background: #e8e8e8; filter:alpha(opacity=85); opacity:.85; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=85)"; } + +.g-movie { border: 1px solid #888; padding: 5px; background: #e8e8e8; } + +/* styles.css - Reauthentificate ~~~~~~~~~~~~~~~~~~~~~*/ + +#g-reauthenticate-form ul { border: 1px #888 solid; } + +/* styles.css - Sidebar Blocks ~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-toolbar { border-bottom: 1px solid #ccc; } + +/* styles.css - Sidebar Blocks : Common ~~~~~~~~~~~~~~*/ + +.g-block { border: 1px solid #ccc; } +.g-block h2 { background-color: #e8e8e8; } + +/* styles.css - Sidebar Blocks : Buttons ~~~~~~~~~~~~~*/ + +#g-viewformat .g-viewthumb-left { background: url('images/view-left.png') no-repeat left top; } +#g-viewformat .g-viewthumb-right { background: url('images/view-right.png') no-repeat left top; } +#g-viewformat .g-viewthumb-full { background: url('images/view-full.png') no-repeat left top; } + +#g-slideshow-link { background: url("images/view-slideshow.png") top left no-repeat; } +.g-fullsize-link { background: url("images/view-fullsize.png") top left no-repeat; } +#g-exifdata-link { background: url("images/view-info.png") top left no-repeat; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* forms.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-body { background: #fff url('images/ajax-loading.gif') no-repeat center center; } +#sb-title { border-left: #303030 1px solid; border-right: #303030 1px solid; background: #e8e8e8; color: #000; } +#sb-title-inner { color: #000; } + +#sb-content.html_ajax p.g-error { color: red; } +#sb-content.html_ajax form { background-color: #fff; } +#sb-content.html_ajax>div { background-color: #fff; } + +/* forms.css - Permissions ~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-permissions .g-breadcrumbs { border: #303030 1px solid; } +#sb-content #g-edit-permissions-form { border: #303030 1px solid; } +#sb-content #g-move>ul { border: #303030 1px solid; } + +/* forms.css - Add item ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-add-photos-form .g-breadcrumbs { border: #303030 1px solid; } + +#g-add-photos-canvas { background-color: #fff; border: #303030 1px solid; } +#g-add-photos-button { border: #303030 1px solid; } +#g-add-photos-status { background-color: #fff; border: #303030 1px solid; } + +#g-add-photos-status li.g-success { background: #d9efc2 url('images/ico-success.png') no-repeat .4em 50%; } +#g-add-photos-status li.g-error { background: #f6cbca url('images/ico-error.png') no-repeat .4em 50%; color: #f00; } + +/* forms.css - Organize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content.html_ajax #g-organize { border: #303030 1px solid; } + +#g-organize-detail { border-left: #303030 1px solid; } +#g-organize .g-message-block { border-bottom: #303030 1px solid; } +.g-organize-microthumb-grid-cell { background-color: #fff; } +.g-organize-microthumb { background-color: #fff; } +#g-organize-controls { border-top: #303030 1px solid; } + +/* forms.css - User Profile ~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-user-profile h1 { border-bottom: #ccc 1px solid; } +#g-user-profile .g-avatar { border: 1px solid #888; background: #fff; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* menus.css ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-site-menu ul { border: #000000 0 solid; } +#g-site-menu li { background-color: #bdd2ff; } +#g-site-menu li a:hover { color: #000000; background-color: #cfdeff; } +#g-site-menu li:hover, +#g-site-menu li.iemhover { border: #303030 1px solid; background-color: #cfdeff; border-bottom: #cfdeff 1px solid; } +#g-site-menu li ul { border: #cfdeff 1px solid; } +#g-site-menu li ul li { border: #C0C0C0 0px solid; background-color: #bdd2ff; } +#g-site-menu li ul li:hover, +#g-site-menu li ul li.iemhover { border: #C0C0C0 0 solid; background-color: #ddf2ff; } + +.g-item .g-context-menu { background-image: url(images/ui-icons.png); } +.g-item .g-context-menu:hover { background: #bdd2ff none; border: 1px #888 solid; } +.g-item .g-context-menu li li a:hover { background-color: #ddf2ff; } + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* modules.css - Exif ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-exif-data table { border: #303030 1px solid; } +#sb-content #g-exif-data .g-even { background-color: #A0A0A0; } +#sb-content #g-exif-data .g-odd { background-color: #C0C0C0; } + +/* modules.css - Info module ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-metadata .g-description { border-top: 1px solid #ccc; } + +/* modules.css - Image block ~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-image-block img { border: 1px solid #888; background: #555; } + +/* modules.css - Comments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-comments .g-author { border-bottom: 1px solid #202628; color: #999; } +#g-comments-link { background-image: url(images/view-comments.png); } +#g-comment-detail>ul>li { border: 1px dotted #ccc; } +#g-comment-form { border: 1px dotted #ccc; } + +/* modules.css - Calendar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-view-menu #g-calendarview-link { background-image: url(images/view-calendar.png); } +#g-view-calendar-form ul { border: 1px #888 solid; } +table.calendar { border: #a2adbc 1px solid; color: #616b76; } +table.calendar th { border-bottom: #a2adbc 1px solid; border-right: #a2adbc 1px solid; background: #d9e2e1; color: #616b76; } +table.calendar td { border-bottom: #a2adbc 1px solid; border-right: #a2adbc 1px solid; } +table.calendar td.title { background-color: #a2adbc; color: #fff; } +table.calendar td.title a { color: #fff !important; } +table.calendar td a { color: red !important; } + +/* modules.css - Search ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-quick-search-form input[type="text"] { background-color: transparent; border: 1px solid #ccc; color: #666; } +#g-quick-search-form input[type="submit"] { border: #c5dbec 1px solid; text-indent: 0; width: auto; height: auto; font: 80% arial, helvetica, clean, sans-serif; font-weight: bold; padding-top: 3px; padding-bottom: 3px; } +#g-search-results h1 { border-bottom: #ccc 1px solid; } + +/* modules.css - Basket ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#checkout legend { background-color: #e8e8e8; } \ No newline at end of file diff --git a/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif b/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif new file mode 100644 index 00000000..53dd589f Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif differ diff --git a/themes/greydragon/css/colorpacks/wind/images/ico-album.png b/themes/greydragon/css/colorpacks/wind/images/ico-album.png new file mode 100644 index 00000000..ac87ec4f Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/ico-album.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/ico-error.png b/themes/greydragon/css/colorpacks/wind/images/ico-error.png new file mode 100644 index 00000000..c37bd062 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/ico-error.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/ico-separator.png b/themes/greydragon/css/colorpacks/wind/images/ico-separator.png new file mode 100644 index 00000000..3e158515 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/ico-separator.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/ico-success.png b/themes/greydragon/css/colorpacks/wind/images/ico-success.png new file mode 100644 index 00000000..a9925a06 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/ico-success.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/ui-icons.png b/themes/greydragon/css/colorpacks/wind/images/ui-icons.png new file mode 100644 index 00000000..a8bd54b5 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/ui-icons.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-calendar.png b/themes/greydragon/css/colorpacks/wind/images/view-calendar.png new file mode 100644 index 00000000..13e0e8fa Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-calendar.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-comments.png b/themes/greydragon/css/colorpacks/wind/images/view-comments.png new file mode 100644 index 00000000..f33bdf19 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-comments.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-full.png b/themes/greydragon/css/colorpacks/wind/images/view-full.png new file mode 100644 index 00000000..e465d366 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-full.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png b/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png new file mode 100644 index 00000000..58b3e0b4 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-info.png b/themes/greydragon/css/colorpacks/wind/images/view-info.png new file mode 100644 index 00000000..2cc7a68e Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-info.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-left.png b/themes/greydragon/css/colorpacks/wind/images/view-left.png new file mode 100644 index 00000000..b51e3368 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-left.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-right.png b/themes/greydragon/css/colorpacks/wind/images/view-right.png new file mode 100644 index 00000000..bd72adfc Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-right.png differ diff --git a/themes/greydragon/css/colorpacks/wind/images/view-slideshow.png b/themes/greydragon/css/colorpacks/wind/images/view-slideshow.png new file mode 100644 index 00000000..61bf18e1 Binary files /dev/null and b/themes/greydragon/css/colorpacks/wind/images/view-slideshow.png differ diff --git a/themes/greydragon/css/forms.css b/themes/greydragon/css/forms.css index e1fb0c38..ab246779 100644 --- a/themes/greydragon/css/forms.css +++ b/themes/greydragon/css/forms.css @@ -1,33 +1,107 @@ -#sb-content { padding: 0px; margin: 0; } - -#g-exif-data { width: auto; min-height: 90%; margin: 10px; text-align: center; color: #ccc; background-color: #101415; overflow: auto; } -#g-exif-data table { border: #eee 1px solid; font-size: 10pt; } -#g-exif-data .g-even { background-color: #aaa; color: #000; } -#g-exif-data .g-odd { background-color: #999; color: #fff; } - -/* Login dialog ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -form { background: #101415 url('../images/section.png') repeat-x; overflow: hidden; } -form fieldset { border: none; } -form legend { color: #bbb; padding: 6px 0 0 10px; width: 100%; } -form ul { padding: 0; } -form li { padding: 8px 0 0 20px; } -form ul>fieldset>legend { display: none; } -form label { display: block; } -form textarea { width: 98%; } -form input[type="text"], -input[type="password"] { width: 90%; } - -#sb-content.html_ajax textarea { width: 270px; } -#sb-content.html_ajax p.g-error { padding-top: 4px; color: red; } - -#g-text { min-height: 70px; } - -#g-login-form { width: 100%; } -#g-login form ul { min-height: 160px; } -#g-edit-user-form ul { min-height: 276px; } -#g-password-reset { margin-left: 8px; } -#g-edit-album-form fieldset fieldset { border: none; } -#g-edit-album-form fieldset fieldset li { float: left; display: inline; } - -#g-add-photos-form { height: 100%; } \ No newline at end of file +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * CSS rules related to forms/dialogs + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* forms.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +input[type="submit"], .g-button, button { cursor: pointer; /* hand-shaped cursor */ cursor: hand; /* for IE 5.x */ font-size: 0.8em; color: #333 !important; padding: 2px 10px; margin-top: 0.4em; border: 1px solid; border-color: #999 #666 #666 #999; background-color: #ddd; font-weight: normal; } + +#sb-content.html_ajax { padding: 0 0.8em; margin: 0; } +#sb-content.html_ajax p.g-error { padding-top: 0.4em; } + +#sb-content.html_ajax form { background-color: #101415; overflow: hidden; } +#sb-content.html_ajax form fieldset { border: none; } +#sb-content.html_ajax form legend { display: none; width: 100%; } +#sb-content.html_ajax form ul { padding: 0; } +#sb-content.html_ajax form li { padding-top: 0.2em; } +#sb-content.html_ajax form>fieldset>ul { margin: 0 2px; } +#sb-content.html_ajax form label { display: block; padding: 0.2em 0; } +#sb-content.html_ajax form textarea { width: 99%; height: 4em; } +#sb-content.html_ajax input[type="submit"]{ margin: 6px 0; } +#sb-content.html_ajax input[type="text"], +#sb-content.html_ajax input[type="password"] { width: 99%; } +#sb-content.html_ajax>div { height: 94%; padding-top: 0.2em; overflow: auto; } +#sb-content #g-text { min-height: 6em; } + +#sb-content fieldset fieldset { border: none; } +#sb-content fieldset fieldset li { float: left; display: inline; margin-right: 1em; } + +/* forms.css - Login ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-login-form { width: 100%; } +#sb-content #g-login form ul { min-height: 10em; } +#sb-content #g-password-reset { margin-left: 0.4em; } + +/* forms.css - Edit Permissions ~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-permissions fieldset { border: none; margin: 1px; overflow: auto; width: 100%; } +#sb-content #g-permissions .g-breadcrumbs { position: static; padding: 0.4em; font-size: small; margin: 0.4em 0; } +#sb-content #g-permissions .g-breadcrumbs .g-first { padding-left: 0; } + +#sb-content #g-edit-permissions-form { margin: 0.4px 0; } +#sb-content #g-edit-permissions-form>fieldset>legend { display: none; } +#sb-content #g-edit-permissions-form>fieldset>table { font-size: small; } +#sb-content #g-edit-permissions-form>fieldset>table th, +#sb-content #g-edit-permissions-form>fieldset>table td { padding: 1px 2px; } + +/* forms.css - Delete Item ~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-confirm-delete { height: 5em; padding: 0.8em 0 0 0; } + +/* forms.css - Move Item ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-move>ul { height: 290px; margin-bottom: 0.4em; padding: 10px; overflow: auto; } +#sb-content #g-move>form { background: none; } + +/* forms.css - Add photo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-add-photos-form { height: 96%; } +#sb-content #g-add-photos-form .g-breadcrumbs { position: static; margin: 4px 0 0 0; padding: 4px; font-size: x-small; } +#sb-content #g-add-photos-form .g-breadcrumbs li { padding-top: 0; } +#sb-content #g-add-photos-form .g-breadcrumbs .g-first { padding-left: 0; } + +#g-add-photos-canvas { margin-top: 4px; height: 100px; } +#g-add-photos-button { padding: 2px 8px; z-index: 10; zoom: 1; } +#g-uploadifyUploader { z-index: 1005; zoom: 1; } +#g-uploadifyQueue { overflow: auto; height: 100%; } +#g-add-photos-status { margin-top: 4px; height: 90px; overflow: auto; } +#g-add-photos-status #g-action-status { margin: 0 0 1px 0; width: 100%; } +#g-add-photos-status #g-action-status li { margin: 0 0 1px 0; padding: 2px 0; text-indent: 30px; width: 100%; } + +/* forms.css - Organize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content.html_ajax #g-organize { height: 440px; } +#g-organize #g-organize-content-pane { display: block; height: 440px; width: 690px; margin: 0 !important; overflow: hidden; } +#g-organize #g-organize-content-pane>div { float: left; height: 440px; } +#g-organize #g-organize-content-pane #g-organize-tree-container { overflow: auto; width: 164px; height: 428px; padding: 0 2px 0 4px !important; } +#g-organize #g-organize-detail { width: 518px; } +#g-organize #g-organize-detail .g-message-block li { padding: 0; } +#g-organize #g-organize-tree-container>ul { font-size: x-small; } +#g-organize #g-organize-tree-container>ul ul { padding: 0px; } +#g-organize #g-organize-album-tree { padding: 0; } +#g-organize .g-message-block { padding: 4px 0 4px 10px; } +#g-organize-microthumb-panel { background-color: transparent; border: none; height: 360px; } +#g-organize-microthumb-grid { position: static; height: 360px; border-style: none; padding: 0 2px !important; } +.g-organize-microthumb-grid-cell { float: left; margin: 2px; } +.g-organize-microthumb-grid-cell .ui-icon-note { background-position: -194px -144px; left: 8px; bottom: 4px; } +#g-organize-controls { position: absolute; background-color: transparent; padding: 6px 10px; } +#g-organize-controls li { display: inline; } +.g-organize-album-text { border: transparent 1px solid; } +#g-organize-close { display: none; } + +/* forms.css - User Profile ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-user-profile h1 { padding-bottom: 1px; margin: 0 0; } +#g-user-profile>div { margin: 2em 0 1em 10em; } +#g-user-profile .g-block-content { text-align: left; } +#g-user-profile .g-avatar { float: left; padding: 2px; } + +#g-user-profile th { text-align: left; padding-right: 20px; } +#g-change-email-user-form { min-height: 200px; } +#g-edit-user-form ul { min-height: 200px; } + +#g-quick-search-form input[type="submit"] { filter: none; margin-top: 0; } \ No newline at end of file diff --git a/themes/greydragon/css/layout.css b/themes/greydragon/css/layout.css index 18b76412..db55e4af 100644 --- a/themes/greydragon/css/layout.css +++ b/themes/greydragon/css/layout.css @@ -1,21 +1,38 @@ -html { overflow: auto; } -* { margin: 0px; } -body { min-width: 73em; padding: 0; margin: 0; } - -#g-header { position: relative; min-width: 73em; z-index: 5; } -#g-main, #g-main-in { min-width: 72.7em; height: auto; bottom: auto; } -#g-footer { position: relative; height: auto; min-width: 73em; clear: both; display: block; overflow: auto; } - -#g-column-left { float: left; width: 18em; min-height: 32em; overflow: hidden; height: 100%; } -#g-column-right { float: right; width: 18em; min-height: 32em; overflow: hidden; height: 100%; } -#g-column-center { margin: 0 19em 0 19em; min-height: 32em; overflow: hidden; height: 100%; } -#g-column-centerleft { margin: 0 19em 0 0; min-height: 32em; overflow: hidden; height: 100%; } -#g-column-centerright { margin: 0 0 0 19em; min-height: 32em; overflow: hidden; height: 100%; } -#g-column-centerfull { position: relative; margin: 0 0; min-height: 31em; overflow: hidden; height: 100%; } - -#g-footer-leftside { float: left; display: inline; } -#g-footer-rightside { float: right; display: inline; } - -.g-hideitem { display: none; } - -#g-main-in { overflow: auto; height: 100%; } \ No newline at end of file +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * CSS rules related to general layout + * Defined as 70em wide + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* layout.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +html { overflow: auto; overflow: -moz-scrollbars-vertical; overflow-y: scroll; } +* { margin: 0px; } +body { min-width: 70em; padding: 0; margin: 0; } + +.g-hideitem { display: none; } + +/* layout.css - Header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-header { position: relative; min-width: 70em; z-index: 5; } + +/* layout.css - Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-main { min-width: 69.7em; height: auto; bottom: auto; } +#g-main-in { min-width: 69.7em; height: 100%; overflow: auto; bottom: auto; } +#g-column-left { float: left; width: 16em; min-height: 32em; overflow: hidden; height: 100%; } +#g-column-right { float: right; width: 16em; min-height: 32em; overflow: hidden; height: 100%; } +#g-column-center { margin: 0 17em 0 17em; min-height: 32em; overflow: hidden; height: 100%; } +#g-column-centerleft { margin: 0; min-height: 32em; overflow: hidden; height: 100%; } +#g-column-centerright { margin: 0; min-height: 32em; overflow: hidden; height: 100%; } +#g-column-centerfull { position: relative; margin: 0 0; min-height: 31em; overflow: hidden; height: 100%; } + +/* layout.css - Footer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-footer { position: relative; height: auto; min-width: 70em; min-height: 4em; clear: both; display: block; overflow: auto; } +#g-footer-leftside { float: left; display: inline; } +#g-footer-rightside { float: right; display: inline; } + diff --git a/themes/greydragon/css/menus.css b/themes/greydragon/css/menus.css index 1e27610c..3ba173f2 100644 --- a/themes/greydragon/css/menus.css +++ b/themes/greydragon/css/menus.css @@ -1,27 +1,56 @@ -#g-site-menu { position: absolute; bottom: 0px; left: 310px; } -#g-site-menu ul { float: left; padding: 0px; margin: 0px; width: 100%; border: #000000 0px solid; white-space: nowrap; z-index: 100; } -#g-site-menu a { display: block; padding: 3px 5px 4px 5px; text-align: center; width: auto; letter-spacing: 0px; cursor: pointer; } -#g-site-menu li { float: left; padding: 0px; background-color: transparent; border: transparent 1px solid; } -#g-site-menu li a:hover { color: #000000; cursor: pointer; background-color: #303030; } -#g-site-menu li:hover, -#g-site-menu li.iemhover { border: #303030 1px solid; background-color: #303030; border-bottom: #000000 1px solid; } -#g-site-menu li ul a { text-align: left; padding: 4px 0px; text-indent: 8px; letter-spacing: 0px; cursor: pointer; } -#g-site-menu li ul a:hover { background-image: none; cursor: pointer; } -#g-site-menu li ul { border: #000000 1px solid; position: absolute; margin: 0px 0px 0px -1px; width: 135px; height: auto; left: -999em; } -#g-site-menu li ul li { border: #C0C0C0 0px solid; background-color: #212121; } -#g-site-menu li ul li:hover, -#g-site-menu li ul li.iemhover { border: #C0C0C0 0px solid; background-color: #303030; } - -#g-site-menu li li { width: 135px; padding-right: 0px; } -#g-site-menu li ul a { width: 135px; } -#g-site-menu li ul ul { margin: -21px 0px 0px 135px; } -#g-site-menu li:hover ul ul, -#g-site-menu li:hover ul ul ul, -#g-site-menu li.iemhover ul ul, -#g-site-menu li.iemhover ul ul ul { left: -999em; } -#g-site-menu li:hover ul, -#g-site-menu li li:hover ul, -#g-site-menu li li li:hover ul, -#g-site-menu li.iemhover ul, -#g-site-menu li li.iemhover ul, -#g-site-menu li li li.iemhover ul { left: auto; } +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * CSS rules related to menus + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* menus.css - Main menu ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-site-menu { position: absolute; left: 24em; } +#g-site-menu.default { bottom: 0; } +#g-site-menu.top { top: 0; } +#g-site-menu ul { float: left; padding-left: 0; width: 100%; white-space: nowrap; z-index: 10; } +#g-site-menu ul ul ul { padding-top: 0; } +#g-site-menu a { display: block; padding: 0.2em 0.4em; text-align: center; width: auto; letter-spacing: 0; cursor: pointer; } +#g-site-menu li { float: left; padding: 0; background-color: transparent; border: transparent 1px solid; z-index: 10; } +#g-site-menu li a:hover { cursor: pointer; } +#g-site-menu li ul a { text-align: left; padding: 0.3em 0; text-indent: 0.8em; letter-spacing: 0; cursor: pointer; } +#g-site-menu li ul a:hover { background-image: none; cursor: pointer; } +#g-site-menu li ul { position: absolute; margin: 0 0 0 -1px; width: 14em; height: auto; left: -999em; } + +#g-site-menu li li { width: 14em; padding-right: 0; } +#g-site-menu li ul a { width: 14em; } +#g-site-menu li ul ul { margin: -1.9em 0 0 14em; } +#g-site-menu li:hover ul ul, +#g-site-menu li:hover ul ul ul, +#g-site-menu li.iemhover ul ul, +#g-site-menu li.iemhover ul ul ul { left: -999em; } +#g-site-menu li:hover ul, +#g-site-menu li li:hover ul, +#g-site-menu li li li:hover ul, +#g-site-menu li.iemhover ul, +#g-site-menu li li.iemhover ul, +#g-site-menu li li li.iemhover ul { left: auto; } + +#g-site-menu>ul>li>ul { display: none; } + +#g-site-menu .ui-icon-rotate-ccw, +#g-site-menu .ui-icon-rotate-cw { display: none; } + +/* menus.css - Context menu ~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-item .g-context-menu { position: absolute; margin: 0; padding: 0; top: 6px; left: 196px; width: 14px; height: 14px; background-position: -178px -144px; z-index: 3; } +.g-item .g-context-menu li { width: 100%; padding: 0; margin: 0; text-indent: -9999px; } +.g-item .g-context-menu>li>a { font-size: 0em; } +.g-item .g-context-menu:hover { top: 4px; left: 6px; width: 200px; height: auto; z-index: 100; } +.g-item .g-context-menu ul { padding: 0; margin: 0; } +.g-item .g-context-menu li li { display: none; } +.g-item .g-context-menu li li a { display: block; padding: 4px 6px; } +.g-item .g-context-menu:hover li li { display: block; text-indent: 0px; } +.g-item .g-context-menu li li a.ui-icon-rotate-ccw, +.g-item .g-context-menu li li a.ui-icon-rotate-cw { display: none; } + +.g-item.g-detail .g-context-menu { left: auto; right: 6px; } +.g-item.g-detail .g-context-menu:hover { left: auto; right: 6px; } \ No newline at end of file diff --git a/themes/greydragon/css/modules.css b/themes/greydragon/css/modules.css new file mode 100644 index 00000000..371434f8 --- /dev/null +++ b/themes/greydragon/css/modules.css @@ -0,0 +1,135 @@ +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * CSS rules related to modules + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* modules.css - ShadowBox Skin ~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-title { overflow: hidden; } +#sb-title-inner { font-size: 10pt; font-weight: bold; padding-left: 10px; } +#sb-nav #sb-nav-close { background-image: url('../images/close.png'); width: 60px; } +#sb-container > #sb-overlay { min-height: 530px; overflow: auto; } + +/* modules.css - Exif Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#sb-content #g-exif-data { width: auto; background-image: none; } +#sb-content #g-exif-data table { width: 100%; } +#sb-content #g-exif-data td { padding: 0.4em; } + +/* modules.css - Image Block ~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-image-block>div { margin-left: 1px; margin-right: 1px; } +.g-image-block { text-align: center; } +.g-image-block img { padding: 5px; } + +/* modules.css - RSS Feeds ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +ul#g-feeds { padding: 0; margin: 0; } + +/* modules.css - Tags ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-tag-cloud ul { padding: 0; font-size: 100%; } +#g-tag-cloud ul li { line-height: 1.2em; } +#g-tag-cloud ul li span { display: none; } +#g-add-tag-form { display: none; } + +/* modules.css - Comments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-comments { margin-top: 2em; padding-top: 0.4em; float: left; width: 100%; } +#g-comments ul li { margin: 0.4em 0; } +#g-comments .g-author { height: 32px; line-height: 32px; } +#g-comments .g-avatar { height: 32px; margin-right: .4em; width: 32px; } + +#g-admin-comment-button { width: 27px; right: 0.2em; text-indent: -900em; } +#g-comments-link { background-position: top left; background-repeat: no-repeat; } +#g-comments-link:hover { background-position: left bottom; } +#g-comment-detail ul { margin-top: 2em; padding: 0; } +#g-comment-detail>ul>li { margin: 4px 0; padding: 6px; min-height: 40px; } +#g-comment-detail div { margin-top: 6px; padding-bottom: 8px; } + +#g-comment-form fieldset { border: none; } +#g-comment-form legend { display: none; width: 100%; } +#g-comment-form ul { padding: 0; } +#g-comment-form>fieldset>ul { margin: 0px 10px; } +#g-comment-form label { display: block; } +#g-comment-form textarea { width: 99%; height: 140px; } +#g-comment-form input[type="text"], +#g-comment-form input[type="password"] { width: 99%; } + +/* modules.css - Gallery Stats ~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-gallerystats ul { padding: 0; font-size: x-small; } + +/* modules.css - Info ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-metadata ul { padding: 0; } +#g-metadata .g-description { margin-top: 0.4em; padding: 0.4em 0; } + +/* modules.css - Calendar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-calendarview-link:hover { background-position: left bottom; } + +#g-view-calendar-form fieldset { border: none; } +#g-view-calendar-form ul { padding: 8px; } +#g-view-calendar-form li { padding-top: 8px; display: inline; padding-left: 10px; } +#g-view-calendar-form label { margin: 4px 0; } +#g-view-calendar-form select { margin: 4px 10px; } + +table.calendar { border-spacing: 1px; } +table.calendar td.title a { font-weight: bold; } + +/* modules.css - ClustrMaps ~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-clustrmaps .g-block-content { text-align: center; } + +/* modules.css - GPS Info ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-exif-gps-maps ul { padding-left: 0; } + +/* modules.css - Search ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-quick-search-form { position: absolute; top: 3em; right: 1em; background: none transparent; } +#g-quick-search-form label { display: none; } +#g-quick-search-form li { display: inline; float: left; padding: 0px; } + +#g-quick-search-form input[type="text"] { width: 150px; } +#g-quick-search-form input[type="submit"] { display: block; width: 23px; height: 23px; text-indent: -9999px; overflow: hidden; } + +/* modules.css - Basket ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.basketbuttons span.ui-icon { display: none; } +#payment { height: 100%; margin-left: 10px; } +#payment p { padding: 4px; } +#basketForm { width: 100%; float:right; } +#checkout { } +#checkout fieldset { border: none; } +#checkout legend { width: 100%; padding: 4px 4px 4px 8px; font-size: 1em; font-weight: bold; } +#checkout ul { padding: 8px; } +#checkout li { padding-top: 8px; display: inline; } +#checkout label { margin: 4px 0; } +#checkout select { margin: 4px 10px; } + +#checkout textarea { display: block; clear: both; padding: .2em; width: 90%; } + +/* modules.css - Register ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-welcome-message p { padding-bottom: 6px; } +#g-change-password-user-form { height: 100%; } + +/* modules.css - Localization ~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#l10n-client .labels { border-top: white 1px solid; height: 1.7em; } +#l10n-client h2 { padding-top: 0.4em; padding-bottom: 0.3em; } +#l10n-client .label.translation { margin-top: -0.4em; height: 1.7em; } +#l10n-client #l10n-client-toggler { line-height: 1.7em; height: 1.7em; } +#l10n-client .string-list li { font-size: 0.8em; line-height: 1.1em; } +#l10n-client #l10n-client-string-select { width: 24%; } +#l10n-client #l10n-client-string-select .string-list { border: 1px #ccc solid; } +#l10n-client #g-l10n-search-form ul { padding: 0; } +#l10n-client #l10n-client-string-editor { margin-left: 1em; } +#l10n-client-string-editor .source .source-text { margin: 0 0.4em 0 0; border: 1px #ccc solid; padding: 0.4em; line-height: 1em; } +#l10n-client-string-editor .translation { height: 19em; } +#l10n-client #l10n-edit-translation { width: 97%; height: 17em; border: 1px #ccc solid; font-family: monospace; padding: 0.4em; } diff --git a/themes/greydragon/css/old_ie.css b/themes/greydragon/css/old_ie.css index 70556d49..9a5da7b8 100644 --- a/themes/greydragon/css/old_ie.css +++ b/themes/greydragon/css/old_ie.css @@ -1,4 +1,16 @@ -body { word-wrap: break-word; } - -.g-item .g-metadata:hover { padding: 0px 0 4px 6px; } -#g-quick-search-form input[type="submit"] { padding: 60px 0 0 0; } +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * CSS rules - IE 6 hacks + */ + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* old_ie.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +body { word-wrap: break-word; font-size: 100.1%; } + +.g-item .g-metadata:hover { padding: 0px 0 4px 6px; } +#g-quick-search-form input[type="submit"] { padding: 60px 0 0 0; } +#g-column-centerleft { margin: 0 19em 0 0; } +#g-column-centerright { margin: 0 0 0 19em; } diff --git a/themes/greydragon/css/screen.css b/themes/greydragon/css/screen.css index 974521a1..17ecf082 100644 --- a/themes/greydragon/css/screen.css +++ b/themes/greydragon/css/screen.css @@ -1,211 +1,224 @@ -/** - * Gallery 3 Grey Dragon Theme - * Copyright (C) 2006-2009 Serguei Dosyukov - * - * 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. - */ - -@import url(layout.css); -@import url(forms.css); - -html { background-color: #1A2022; overflow: -moz-scrollbars-vertical; overflow-y: scroll; } -body { background: url(../images/background.gif) #1A2022 repeat-x; color: #BBB; font: 0.8em Arial, verdana, sans-serif; } - -a { color: #6392CF !important; text-decoration: none; outline: none; -moz-outline-style: none; } -a:focus, a:active, a:hover { text-decoration: none; outline: none; } -img { border: none; } -p { font-size: small; text-indent: 0; } -ul { list-style: none none; } -input[type="submit"] { cursor: pointer; /* hand-shaped cursor */ cursor: hand; /* for IE 5.x */ } - -h1 { font-weight: bold; font-size: 1.2em; } -h2 { font-weight: bold; font-size: 1.2em; } -h3 { font-weight: bold; } -h4 { font-weight: bold; } -h5 { font-weight: bold; } - -/* Common elements ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -.txtright { text-align: right; } -.g-metadata { overflow: hidden; } - -.ui-icon { display: inline-block; zoom: 1; width: 16px; height: 16px; background-image: url(../images/ui-icons.png); } -.ui-icon-first { background-position: -32px -162px; } -.ui-icon-first-d { background-position: -162px -162px; } -.ui-icon-prev { background-position: -48px -162px; } -.ui-icon-prev-d { background-position: -178px -162px; } -.ui-icon-next { background-position: -64px -162px; } -.ui-icon-next-d { background-position: -194px -162px; } -.ui-icon-last { background-position: -80px -162px; } -.ui-icon-last-d { background-position: -210px -162px; } -.ui-icon-signal-diag { background-position: -16px -178px; } -.ui-icon-info { background-position: -16px -144px; } -.ui-icon-comment { background-position: -227px -219px; width: 27px; height: 20px; } -.ui-icon-left .ui-icon { float: left; margin-right: .2em; } -.ui-icon-right .ui-icon { float: right; margin-left: .2em; } - -.g-resize { border: 1px solid #888; padding: 5px; background: #555; } - -/* Header section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -#g-header { height: 90px; padding: 0; font-size: 80%; } -#g-logo { position: absolute; top: 8px; left: 16px; } - -#g-login-menu { position: absolute; bottom: 10px; right: 14px; background-color: transparent; } -#g-login-menu li { display: inline; padding-left: 1.2em; } - -.g-breadcrumbs { position: absolute; bottom: 4px; right: 14px; background-color: transparent; } -.g-breadcrumbs li { display: inline; padding-left: 1em; background: transparent url('../images/ico-separator.png') no-repeat 0 2px; } -.g-breadcrumbs li.g-first { background-image: none; } - -/* Main section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -#g-main { display: block; margin: 0; } -#g-main-in { display: block; position: relative; } - -#g-column-center, #g-column-centerleft { padding: 6px 6px 6px 16px; } -#g-column-centerfull { padding: 6px 12px 6px 10px; } -#g-column-centerright { padding: 6px 12px 6px 6px; } -#g-column-left { padding: 6px 4px 6px 10px; } -#g-column-right { padding: 6px 10px 6px 4px; } - -/* Pagination ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -.g-paginator { display: inline-block; width: 100%; padding: 4px 0 0 0; font-size: 80%; zoom: 1; } -.g-paginator li { display: inline; float: left; margin-left: 0; zoom: 1; } -.g-paginator a { padding: 0 0 0 2px; } - -.g-paginator .g-pagination { width: 80%; padding-top: 2px; } -.g-paginator .g-navigation { text-align: right; width: 20%; } - -/* Album grid ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -.g-thumbcrop { overflow: hidden; position: relative; width: 200px; height: 150px; } -#g-album-grid { padding: 6px 0 0 0; width: 100%; } -#g-album-grid .g-item { position: relative; float: left; padding: 10px 9px 0px 9px; width: 30.5%; height: 190px; background: url('../images/image_thumb.gif') no-repeat; } -#g-album-grid .g-item p { text-align: center; } -#g-album-grid h2 { position: absolute; top: 164px; left: 12px; width: 150px; font: 100%/100% Arial, Helvetica, sans-serif; } -#g-album-grid h2 a { display: block; margin-top: 4px; font: bold 70% Arial, Helvetica, Verdana, Sans-Serif; letter-spacing: 0.1em; text-transform: uppercase; min-height: 2em; } -#g-album-grid .g-album h2 { padding-left: 20px; background: url('../images/ico-album.png') no-repeat 0px 2px; } - -.g-item .g-metadata { display: block; position: absolute; margin: 0; padding: 0; top: 172px; left: 198px; width: 14px; height: 14px; background: url(../images/ui-icons.png) -162px -144px; } -.g-item .g-metadata li { padding: 0; margin: 0; text-indent: -9999px; font: bold 70% Arial, Helvetica, Verdana, Sans-Serif; letter-spacing: 0.1em; } -.g-item .g-metadata:hover { padding: 4px 0 0 6px; top: 148px; left: 6px; width: 198px; height: 32px; background: #181818 none; border: 1px #888 solid; z-index: 100; } -.g-item .g-metadata:hover li { text-indent: 0px; } - -.g-item .g-context-menu { position: absolute; margin: 0; padding: 0; top: 6px; left: 198px; width: 14px; height: 14px; background: url(../images/ui-icons.png) -178px -144px; } -.g-item .g-context-menu li { width: 100%; padding: 0; margin: 0; text-indent: -9999px; font: bold 70% Arial, Helvetica, Verdana, Sans-Serif; letter-spacing: 0.1em; } -.g-item .g-context-menu:hover { top: 4px; left: 6px; width: 204px; height: auto; background: #181818 none; border: 1px #888 solid; z-index: 100; } -.g-item .g-context-menu ul { display: block; padding: 0; margin: 0; } -.g-item .g-context-menu li li { display: none; font-size: 100%; width: 100%; } -.g-item .g-context-menu li li a { display: block; padding: 4px 6px; } -.g-item .g-context-menu:hover li li { display: block; text-indent: 0px; } -.g-item .g-context-menu li li a:hover { background-color: #303030; } - -.ul-table { text-align: center; margin: 0px auto; padding: 0; list-style-type: none; clear: both; } -.ul-table li { float: left; text-align: center; } - -#g-info { } -#g-info h1 { padding-bottom: 1px; border-bottom: 1px solid #888; } -#g-info .g-description { display: none; } -/* #g-info h1:hover .g-description { position: relative; z-index: 10; top: 10px; left: 0px; width: 90%; display: block; afloat: left; border: 1px solid #888; padding: 6px; }*/ -#g-photo { padding: 6px 0 6px 6px; text-align: center; } -#g-albumheader h1 { padding-bottom: 1px; margin-bottom: 6px; border-bottom: 1px solid #888; } - -/* Footer section ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -#g-footer { padding: 6px 6px 6px 14px; background: url('../images/footer.png') #1A2022 repeat-x top !important; zoom: 1; font-size: 80%; } -#g-footer ul { float: left; color: #999; padding: 0; text-align: left; } -#g-footer li { padding: 0 0 2px 0; } - -#g-visitors { float: left; display: inline; margin: 3px 4px 3px 12px; } -#g-copyright { font-size: x-small; color: #808080; } - -#g-footer-rightside { padding-right: 6px; text-align: right; } - -/* Design blocks ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#g-quick-search-form { position: absolute; top: 10px; right: 14px; background: none transparent; } -#g-quick-search-form label { display: none; } -#g-quick-search-form li { display: inline; float: left; padding: 0px; } - -#g-quick-search-form input[type="text"] { background-color: transparent; border: 1px solid #737373; color: #BBB; width: 150px; /* margin-left: 2px; */ } -#g-quick-search-form input[type="submit"] { display: block; width: 23px; height: 23px; text-indent: -9999px; background: transparent url(../images/search.png) no-repeat center top; border: none; overflow: hidden; } - -#g-search-results h1 { border-bottom: #888 1px solid; } - -/* Sidebar Blocks ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -/* Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -.g-block { margin-bottom: 0.5em; padding-bottom: 4px; border: 1px solid #737373; background-color: #101415; position: relative; } -.g-block h2 { padding: 4px; font-size: 1.2em; background: url('../images/section.png') repeat-x; } -.g-block-content { margin: 4px 10px 0 10px; display: block; zoom: 1; } - -/* Image Block ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#g-image-block>div { margin-left: 1px; margin-right: 1px; } -.g-image-block { text-align: center; } -.g-image-block img { border: 1px solid #888; background: #555; padding: 5px; } - -/* Feeds Block ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -ul#g-feeds { padding: 0; margin: 0; } - -/* Tags and cloud ~~~~~~~~~~~~~~~~~~~~~~~~ */ -#g-tag-cloud ul { padding: 0; font-size: 100%; } -#g-tag-cloud ul li { line-height: 1.2em; } -#g-tag-cloud ul li span { display: none; } -#g-add-tag-form { display: none; } - -/* Comments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#g-admin-comment-button { width: 27px; right: 10px; text-indent: -900em; } -.g-avatar { float: right; } -#g-comments-link { background: url('../images/view-comments.png') top left no-repeat; } -#g-comments .g-block-content { margin: 0; } -#g-comment-detail ul { padding: 0px; } -#g-comment-detail > ul > li { margin: 4px; padding: 6px; min-height: 40px; border: 1px dotted #737373; } -#g-comment-detail div { margin-right: 48px; margin-top: 8px; } - -/* Buttons ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - -#g-viewformat { z-index: 5; position: absolute; padding: 0; top: 6px; right: 10px; } -#g-viewformat li { float: left; margin-right: 2px; } -#g-viewformat .g-viewthumb-left { background: url('../images/view-left.png') no-repeat left top; } -#g-viewformat .g-viewthumb-right { background: url('../images/view-right.png') no-repeat left top; } -#g-viewformat .g-viewthumb-full { background: url('../images/view-full.png') no-repeat left top; } -#g-viewformat span { line-height: 1px; text-indent: -900em; width: 17px; display: block; height: 15px; } -#g-viewformat span:hover, -#g-viewformat span.g-viewthumb-current { background-position: left bottom; } - -#g-view-menu { position: absolute; top: 6px; right: 70px; height: 16px; z-index: 5; zoom: 1; margin: 0 0 6px 0; padding: 0 0 4px 0; } -.g-toolbar { height: 16px; zoom: 1; margin: 0 0 4px 0; padding: 0 0 3px 0; border-bottom: 1px solid #737373; } -.g-menu { margin: 0; padding: 0; text-align: left; } -.g-menu li { display: inline; } - -.g-menu-element, -.g-menu-link { display: inline; float: left; margin-right: 4px; } - -.g-buttonset ul { height: 16px; } -.g-buttonset .g-menu-link { text-indent: -99999px; width: 22px; height: 15px; } - -#g-slideshow-link { background: url("../images/view-slideshow.png") top left no-repeat; } -.g-fullsize-link { background: url("../images/view-fullsize.png") top left no-repeat; } -#g-exifdata-link { background: url("../images/view-info.png") top left no-repeat; } - -#g-slideshow-link:hover, .g-fullsize-link:hover, #g-exifdata-link:hover, #g-comments-link:hover { background-position: left bottom; } - -/* ShadowBox Skin ~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -#sb-body { background: #101415 url('../images/ajax-loading.gif') no-repeat center center; } -#sb-title-inner { display: none; } -#sb-nav #sb-nav-close { background-image: url('../images/close.png'); width: 60px; } - -.clear { clear: both; margin-top: -1px; height: 1px; overflow: hidden; } - -.g-message-block { position: absolute; z-index: 10; min-width: 30em; padding: 4px 6px; right: 10px; top: 34px; border: 1px #888 solid; background-color: #AAA; overflow: hidden; color: #000; font: bold 9pt Arial, verdana, sans-serif; text-align: center; } +/** + * Gallery 3 Grey Dragon Theme + * Copyright (C) 2006-2010 Serguei Dosyukov + * + * CSS rules - Kitchen sync + * + * Color rules for font/background/lines can be found in dedicated colorpack files + */ + +@import url(layout.css); +@import url(menus.css); +@import url(forms.css); +@import url(modules.css); + +/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* screen.css - Common ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +body { font-family: Arial, verdana, sans-serif; font-size: 0.9em; } + +a { text-decoration: none; outline: none; -moz-outline-style: none; } +a:focus, a:active, a:hover { text-decoration: none; outline: none; } +img { border: none; } +p { text-indent: 0; } +ul { list-style: none none; } + +h1 { font-weight: bold; font-size: 1.1em; padding-bottom: 1px; } +h2 { font-weight: bold; font-size: 1.1em; } +h3 { font-weight: bold; } +h4 { font-weight: bold; } +h5 { font-weight: bold; } + +.txtright { text-align: right; } +.g-metadata { overflow: hidden; } +.g-avatar { float: right; } + +.ui-icon { display: inline-block; zoom: 1; width: 16px; height: 15px; } +.ui-icon-first { background-position: -162px -178px; } +.ui-icon-first-d { background-position: -162px -162px; } +.ui-icon-prev { background-position: -178px -178px; } +.ui-icon-prev-d { background-position: -178px -162px; } +.ui-icon-parent { background-position: -226px -178px; } +.ui-icon-parent-d { background-position: -226px -162px; } +.ui-icon-next { background-position: -194px -178px; } +.ui-icon-next-d { background-position: -194px -162px; } +.ui-icon-last { background-position: -210px -178px; } +.ui-icon-last-d { background-position: -210px -162px; } +.ui-icon-signal-diag { background-position: -16px -178px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-plus { background-position: -14px -129px; } +.ui-icon-minus { background-position: -46px -129px; } +.ui-icon-note { background-position: -66px -98px; } + +.ui-icon-comment { background-position: -227px -219px; width: 27px; height: 20px; } +.ui-icon-left .ui-icon { float: left; margin-right: .2em; } +.ui-icon-right .ui-icon { float: right; margin-left: .2em; } + +/* screen.css - Header ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-header { height: 90px; padding: 0; font-size: 0.9em; } + +#g-logo { position: absolute; top: 8px; left: 16px; } + +.g-breadcrumbs { position: absolute; bottom: 4px; background-color: transparent; } +.g-breadcrumbs.default { right: 14px; } +.g-breadcrumbs.left { left: 304px; padding-left: 0; } +.g-breadcrumbs li { display: inline; padding-left: 1em; padding-right: 0.4em; } +.g-breadcrumbs li.g-first { background-image: none; } +.g-breadcrumbs li.g-active { padding-right: 0; } + +#g-header .g-message-block { position: absolute; z-index: 10; min-width: 30em; padding: 4px 6px; right: 20em; top: 34px; overflow: hidden; font: bold 9pt Arial, verdana, sans-serif; text-align: center; } + +#g-header #g-login-menu { position: absolute; top: 0.5em; right: 1em; background-color: transparent; display: none; } + +/* screen.css - Main ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-main { display: block; margin: 0; } +#g-main-in { display: block; position: relative; } + +#g-column-center, #g-column-centerleft { padding: 6px 6px 6px 16px; } +#g-column-centerfull { padding: 6px 12px 6px 10px; } +#g-column-centerright { padding: 6px 12px 6px 6px; } +#g-column-left { padding: 6px 4px 6px 10px; } +#g-column-right { padding: 6px 10px 6px 4px; } + +/* screen.css - Footer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-footer { padding: 6px 6px 6px 14px; zoom: 1; font-size: 0.9em; } +#g-footer ul { float: left; padding: 0; text-align: left; } +#g-footer li { padding: 0 0 2px 0; } + +#g-footer #g-login-menu { position: absolute; bottom: 0.5em; right: 1em; background-color: transparent; display: none; } + +#g-login-menu li { display: inline; padding-left: 1.2em; } +#g-logout-link { float: none; margin-right: 0; } + +#g-copyright { font-size: x-small; } +#g-footer #g-footer-rightside { float: right; padding-right: 6px; text-align: right; } +#g-credits { margin-right: 14px; } + +/* screen.css - Pagination ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-paginator { display: inline-block; width: 100%; padding: 4px 0 0 0; zoom: 1; } +.g-paginator li { display: inline; float: left; margin-left: 0; zoom: 1; } +.g-paginator a { padding: 0 0 0 2px; } + +.g-paginator .g-pagination { width: 80%; font-size: 0.8em; } +.g-paginator .g-navigation { text-align: right; width: 20%; } + +/* screen.css - Album grid ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-album-grid { padding: 6px 0 0 0; width: 100%; display: inline-block; } +#g-album-grid .g-item { position: relative; float: left; margin: 4px 0; min-width: 212px; width: 33%; zoom: 1; } /* amargin-right: 10px; */ +#g-album-grid .g-extra-column { width: 23%; } +#g-album-grid .g-item p { text-align: center; } +#g-album-grid h2 { position: absolute; top: 164px; left: 12px; width: 150px; font: 100%/100% Arial, Helvetica, sans-serif; } +#g-album-grid h2 a { display: block; margin-top: 4px; font: bold 0.8em Arial, Helvetica, Verdana, Sans-Serif; letter-spacing: 0.1em; text-transform: uppercase; min-height: 2em; } + +/* screen.css - Thumbs : Common ~~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-thumbcrop { overflow: hidden; position: relative; width: 200px; min-height: 133px; } + +.g-thumbtype-flm .g-thumbcrop { height: 150px; } +.g-thumbtype-dgt .g-thumbcrop { height: 133px; } +.g-thumbtype-sqr .g-thumbcrop { height: 200px; } +.g-album .g-description strong { padding-left: 16px; } + +/* screen.css - Thumbs : Overlay ~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-thumbslide { font-size: 0.9em; width: 208px; min-height: 139px; padding-top: 6px; padding-left: 6px; } +.g-thumbslide.g-thumbtype-flm { height: 158px; } +.g-thumbslide.g-thumbtype-dgt { height: 141px; } +.g-thumbslide.g-thumbtype-sqr { height: 208px; } + +.g-thumbcrop a.g-thumlink { display: block; position: relative; } +.g-thumbslide .g-thumbcrop .g-description { display: none; } +.g-thumbslide:hover .g-description { display: block; position: absolute; top: 0; min-height: 32px; width: 100%; overflow: hidden; z-index: 3; font-weight: bold; font-size: 0.9em; letter-spacing: 0.1em; text-transform: uppercase; text-align: left; } +.g-thumbslide:hover .g-description strong { display: block; margin-left: 10px; padding-top: 2px; } +.g-album .g-thumbslide:hover .g-description strong { padding-left: 16px; } +.g-thumbslide .g-description strong { display: block; margin-left: 10px; padding-top: 2px; } + +.g-thumbslide .g-metadata { display: none; } +.g-thumbslide:hover .g-metadata { display: block; position: absolute; bottom: 7px; margin: 0 0 1px 1px; padding: 2px 4px 2px 6px; width: 190px; } +.g-thumbslide:hover .g-metadata li { padding: 0; margin: 0; font-size: 0.9em; } +.g-album .g-thumbslide:hover .g-metadata { bottom: 10px; } + +/* screen.css - Thumbs : Extended View mode ~~~~~~~~~~~~*/ + +.g-thumbslide-ext { font-size: 0.9em; width: 208px; min-height: 139px; padding-top: 6px; padding-left: 6px; } +.g-thumbslide-ext.g-thumbtype-flm { height: 188px; } +.g-thumbslide-ext.g-thumbtype-dgt { height: 171px; } +.g-thumbslide-ext.g-thumbtype-sqr { height: 238px; } + +.g-thumbslide-ext .g-description { display: block; margin-top: 2px; width: 200px; overflow: hidden; font-weight: bold; font-size: 0.9em; letter-spacing: 0.1em; text-transform: uppercase; text-align: left; } +.g-thumbslide-ext .g-description strong { display: block; } +.g-album .g-thumbslide-ext .g-description strong { padding-left: 24px; } + +.g-thumbslide-ext .g-metadata { display: none; } +.g-thumbslide-ext:hover .g-metadata { display: block; position: absolute; bottom: 37px; margin: 0 0 1px 1px; padding: 2px 4px 2px 6px; width: 190px; } +.g-thumbslide-ext:hover .g-metadata li { padding: 0; margin: 0; font-size: 0.9em; } +.g-album .g-thumbslide-ext:hover .g-metadata { bottom: 40px; } + +/* screen.css - Photo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-item { float: left; height: 100%; width: 100%; } +#g-photo { padding: 6px 0 6px 6px; text-align: center; float: left; height: 100%; width: 100%; } +div.g-resize { position: relative; left: 50%; float: left; padding: 5px; font-size: 0.9em; } +div.g-resize>a { float: left; overflow: hidden; } +div.g-resize>a img { float: left; } +div.g-resize .g-description { display: none; } +div.g-resize:hover .g-description { position: absolute; display: block; top: 0px; margin-top: 5px; text-align: left; padding: 10px; } +div.g-resize:hover .g-description strong { display: block; margin-bottom: 5px; text-transform: uppercase; } + +div.g-resize .g-more { display: block; position: absolute; right: 16px; top: 16px; padding: 4px 8px; } +div.g-resize:hover .g-more { display: none; visibility: hidden; } + +.ul-table { text-align: center; margin: 0px auto; padding: 0; list-style-type: none; clear: both; } +.ul-table li { float: left; text-align: center; } + +#g-info { display: inline-block; width: 100%; } +#g-info .g-description { margin-top: 4px; margin-bottom: 4px; padding: 4px; } +#g-movie { padding: 6px 0 6px 6px; position: relative; } + +.g-movie { margin: 0 auto; } + +#g-albumheader h1 { margin-bottom: 6px; } + +.g-description .g-metadata { padding: 0.4em 0 0 0; font-size: 0.8em; } +.g-description .g-metadata li { display: inline; padding-right: 1em; } + +/* screen.css - Sidebar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +/* screen.css - Sidebar : Common ~~~~~~~~~~~~~~~~~~~~~~~*/ + +.g-block { margin-bottom: 4px; padding-bottom: 4px; position: relative; } +.g-block h2 { padding: 4px 4px 4px 8px; font-size: 1em; } +.g-block-content { margin: 4px 6px 0 6px; display: block; zoom: 1; } + +/* screen.css - Sidebar : Buttons ~~~~~~~~~~~~~~~~~~~~~~*/ + +#g-viewformat { z-index: 5; position: absolute; padding: 0; top: 6px; right: 10px; } +#g-viewformat li { float: left; margin-right: 2px; } +#g-viewformat span { line-height: 1px; text-indent: -900em; width: 17px; display: block; height: 15px; } +#g-viewformat span:hover, +#g-viewformat span.g-viewthumb-current { background-position: left bottom; } + +#g-view-menu { position: absolute; top: 6px; right: 70px; height: 16px; z-index: 5; zoom: 1; margin: 0 0 6px 0; padding: 0 0 4px 0; } +#g-view-menu.g-buttonset-shift { right: 6px; } +.g-toolbar { height: 1.1em; zoom: 1; margin: 0 0 4px 0; padding: 1px 0 3px 0; } +.g-menu { margin: 0; padding: 0; text-align: left; } +.g-menu li { display: inline; } + +.g-menu-element, +.g-menu-link { display: inline; float: left; margin-right: 4px; } + +.g-buttonset .g-menu-link { text-indent: -99999px; width: 22px; height: 15px; } + +#g-slideshow-link:hover, .g-fullsize-link:hover, #g-exifdata-link:hover { background-position: left bottom; } + +/* screen.css - Reauthentificate ~~~~~~~~~~~~~~~~~~~~~~ */ + +#g-reauthenticate-form fieldset { border: none; width: 260px; } +#g-reauthenticate-form ul { padding: 8px; } +#g-reauthenticate-form li { padding-top: 8px; } +#g-reauthenticate-form label { display: block; } +#g-reauthenticate-form input[type="password"] { width: 98%; } diff --git a/themes/greydragon/helpers/exif_event.php b/themes/greydragon/helpers/exif_event.php index f61bcb7c..27b617a6 100644 --- a/themes/greydragon/helpers/exif_event.php +++ b/themes/greydragon/helpers/exif_event.php @@ -1,7 +1,7 @@ get("add_menu"); + if (!empty($submenu)) { + $item = $submenu->get("add_photos_item"); + if (!empty($item)) { $item->css_class("ui-icon-plus"); } + + $item = $submenu->get("add_album_item"); + if (!empty($item)) { $item->css_class("ui-icon-note"); } + } + + $submenu = $menu->get("options_menu"); + if (!empty($submenu)) { + $item = $submenu->get("edit_item"); + if (!empty($item)) { $item->css_class("ui-icon-pencil"); } + + $item = $submenu->get("edit_permissions"); + if (!empty($item)) { $item->css_class("ui-icon-key"); } + } + } +} diff --git a/themes/greydragon/helpers/greydragon_installer.php b/themes/greydragon/helpers/greydragon_installer.php new file mode 100644 index 00000000..461e6914 --- /dev/null +++ b/themes/greydragon/helpers/greydragon_installer.php @@ -0,0 +1,30 @@ + \ No newline at end of file diff --git a/themes/greydragon/helpers/greydragon_theme.php b/themes/greydragon/helpers/greydragon_theme.php new file mode 100644 index 00000000..988da98c --- /dev/null +++ b/themes/greydragon/helpers/greydragon_theme.php @@ -0,0 +1,30 @@ +' + . $theme_info->name . ' ' . $theme_info->version . '
    • '; + } +} + diff --git a/themes/greydragon/images/blue-grad.png b/themes/greydragon/images/blue-grad.png new file mode 100644 index 00000000..36e0f6bc Binary files /dev/null and b/themes/greydragon/images/blue-grad.png differ diff --git a/themes/greydragon/images/button-grad-active-vs.png b/themes/greydragon/images/button-grad-active-vs.png new file mode 100644 index 00000000..dc641725 Binary files /dev/null and b/themes/greydragon/images/button-grad-active-vs.png differ diff --git a/themes/greydragon/images/button-grad-vs.png b/themes/greydragon/images/button-grad-vs.png new file mode 100644 index 00000000..51c55a3d Binary files /dev/null and b/themes/greydragon/images/button-grad-vs.png differ diff --git a/themes/greydragon/images/donate.png b/themes/greydragon/images/donate.png new file mode 100644 index 00000000..f36bb57a Binary files /dev/null and b/themes/greydragon/images/donate.png differ diff --git a/themes/greydragon/images/header.png b/themes/greydragon/images/header.png deleted file mode 100644 index b60c4a66..00000000 Binary files a/themes/greydragon/images/header.png and /dev/null differ diff --git a/themes/greydragon/images/ico-album.png b/themes/greydragon/images/ico-album.png deleted file mode 100644 index 01463be4..00000000 Binary files a/themes/greydragon/images/ico-album.png and /dev/null differ diff --git a/themes/greydragon/images/image_thumb.gif b/themes/greydragon/images/image_thumb.gif deleted file mode 100644 index 35b05e63..00000000 Binary files a/themes/greydragon/images/image_thumb.gif and /dev/null differ diff --git a/themes/greydragon/images/missing-img.png b/themes/greydragon/images/missing-img.png new file mode 100644 index 00000000..12b7394f Binary files /dev/null and b/themes/greydragon/images/missing-img.png differ diff --git a/themes/greydragon/images/section.png b/themes/greydragon/images/section.png deleted file mode 100644 index bba5da2c..00000000 Binary files a/themes/greydragon/images/section.png and /dev/null differ diff --git a/themes/greydragon/images/ui-icons.png b/themes/greydragon/images/ui-icons.png deleted file mode 100644 index 199b7fda..00000000 Binary files a/themes/greydragon/images/ui-icons.png and /dev/null differ diff --git a/themes/greydragon/images/view-full.png b/themes/greydragon/images/view-full.png deleted file mode 100644 index e7290319..00000000 Binary files a/themes/greydragon/images/view-full.png and /dev/null differ diff --git a/themes/greydragon/images/view-fullsize.png b/themes/greydragon/images/view-fullsize.png deleted file mode 100644 index 5bff90dd..00000000 Binary files a/themes/greydragon/images/view-fullsize.png and /dev/null differ diff --git a/themes/greydragon/images/view-info.png b/themes/greydragon/images/view-info.png deleted file mode 100644 index edf12b51..00000000 Binary files a/themes/greydragon/images/view-info.png and /dev/null differ diff --git a/themes/greydragon/images/view-left.png b/themes/greydragon/images/view-left.png deleted file mode 100644 index 63e2793e..00000000 Binary files a/themes/greydragon/images/view-left.png and /dev/null differ diff --git a/themes/greydragon/images/view-right.png b/themes/greydragon/images/view-right.png deleted file mode 100644 index d1878dd6..00000000 Binary files a/themes/greydragon/images/view-right.png and /dev/null differ diff --git a/themes/greydragon/images/view-slideshow.png b/themes/greydragon/images/view-slideshow.png deleted file mode 100644 index 8e15cd30..00000000 Binary files a/themes/greydragon/images/view-slideshow.png and /dev/null differ diff --git a/themes/greydragon/js/dialog.js b/themes/greydragon/js/dialog.js deleted file mode 100644 index 1d11a569..00000000 --- a/themes/greydragon/js/dialog.js +++ /dev/null @@ -1,17 +0,0 @@ -function setupLoginForm() { - setupAjaxForm('#gLoginForm'); -} - -function setupAjaxForm($form_id) { - var options = { - dataType: "json", - success: function(data) { - if (data.result == "success") { - if (data.location) { window.location = data.location; } - else { window.parent.Shadowbox.close(); } - } - } - }; - - $($form_id).ajaxForm(options); -}; diff --git a/themes/greydragon/js/menus.js b/themes/greydragon/js/menus.js deleted file mode 100644 index f4f1431a..00000000 --- a/themes/greydragon/js/menus.js +++ /dev/null @@ -1,14 +0,0 @@ -// Javascript originally by Patrick Griffiths and Dan Webb. -// http://htmldog.com/articles/suckerfish/dropdowns/ - -sfHover = function() { - var sfEls = document.getElementById("gSiteMenu").getElementsByTagName("ul")[0].getElementsByTagName("li"); - if (!sfEls) { return; } - - for (var i=0; iul>li>ul").show(); + $("#g-login-menu").show(); + $(".g-context-menu").show(); + }, + +// gallery_dialog_postprocess: function(href, title) { +// Shadowbox.open({player: 'ajax', content: href, width: 500, height: 420, enableKeys: false, animate: false, title: title, onFinish: myAjaxSubmit}); +// } +}); + +/* +(function($) { + + $.widget("ui.gallery_dialog", { + _init: function() { + var self = this; + if (!self.options.immediate) { + this.element.click(function(event) { + event.preventDefault(); + var href = $(event.currentTarget).attr("href"); + var title = $(event.currentTarget).attr("title"); + setTimeout(function() { $().gallery_dialog_postprocess(href, title); }, 1000); + return false; + }); + } else { + var href = this.element.attr("href"); + var title = this.element.attr("title"); + setTimeout(function() { $().gallery_dialog_postprocess(href, title); }, 1000); + } + } + }); +})(jQuery); +*/ + +$(document).ready(function() { + $().theme_ready(); +}); diff --git a/themes/greydragon/libraries/MY_Theme_View.php b/themes/greydragon/libraries/MY_Theme_View.php new file mode 100644 index 00000000..4f579ec2 --- /dev/null +++ b/themes/greydragon/libraries/MY_Theme_View.php @@ -0,0 +1,313 @@ +ensurevalue(module::get_var("th_greydragon", $key), $default)); + } + + public function load_sessioninfo() { + $this->sidebarvisible = $_REQUEST['sb']; + + if (empty($this->sidebarvisible)): + $session = Session::instance(); + $_sidebar_mode = $session->get("gd_sidebar"); + if ($_sidebar_mode): + $this->sidebarvisible = $_sidebar_mode; + else: + $this->sidebarvisible = $this->ensureoptionsvalue("sidebar_visible", "right"); + endif; + else: + // Sidebar position is kept for 360 days + Session::instance()->set("gd_sidebar", $this->sidebarvisible, time() + 31536000); + endif; + + $this->sidebarallowed = $this->ensureoptionsvalue("sidebar_allowed", "any"); + $this->sidebarvisible = $this->ensurevalue($this->sidebarvisible, "right"); + + if ($this->sidebarallowed == "none") { $this->sidebarvisible = $this->ensureoptionsvalue("sidebar_visible", "right"); }; + if ($this->sidebarallowed == "right") { $this->sidebarvisible = "right"; } + if ($this->sidebarallowed == "left") { $this->sidebarvisible = "left"; } + + if ($this->item()): + if ($this->ensureoptionsvalue("sidebar_albumonly", FALSE)): + if (!$this->item()->is_album()): + $this->sidebarallowed = "none"; + $this->sidebarvisible = "none"; + endif; + endif; + endif; + + $this->logopath = $this->ensureoptionsvalue("logo_path", url::file("lib/images/logo.png")); + $this->show_guest_menu = $this->ensureoptionsvalue("show_guest_menu", FALSE); + $this->horizontal_crop = $this->ensureoptionsvalue("horizontal_crop", FALSE); + $this->thumb_descmode = $this->ensureoptionsvalue("thumb_descmode", "overlay"); + $this->photo_descmode = $this->ensureoptionsvalue("photo_descmode", "overlay"); + $this->is_thumbmeta_visible = ((!$this->ensureoptionsvalue("hide_thumbmeta", FALSE)) and module::is_active("info")); + $this->is_photometa_visible = ((!$this->ensureoptionsvalue("hide_photometa", TRUE)) and module::is_active("info")); + $this->disable_seosupport = $this->ensureoptionsvalue("disable_seosupport", FALSE); + $this->is_blockheader_visible = (!$this->ensureoptionsvalue("hide_blockheader", FALSE)); + $this->mainmenu_position = $this->ensureoptionsvalue("mainmenu_position", "default"); + $this->show_breadcrumbs = (!$this->ensureoptionsvalue("hide_breadcrumbs", FALSE)); + $this->loginmenu_position = ($this->ensureoptionsvalue("loginmenu_position", "default")); + $this->copyright = ($this->ensureoptionsvalue("copyright", null)); + $this->photonav_position = module::get_var("th_greydragon", "photonav_position", "top"); + $this->desc_allowbbcode = $this->ensureoptionsvalue("desc_allowbbcode", FALSE); + $this->enable_pagecache = $this->ensureoptionsvalue("enable_pagecache", FALSE); + $this->color_pack = $this->ensureoptionsvalue("color_pack", "greydragon"); + + $cssfile = gallery::find_file("css/colorpacks/" . $this->color_pack, "colors.css", false); + + if (!$cssfile): + $this->color_pack = 'greydragon'; + endif; + + switch (module::get_var("th_greydragon", "thumb_ratio")): + case "digital": + $this->crop_factor = 4/3; + $this->crop_class = 'g-thumbtype-dgt'; + break; + case "square": + $this->crop_factor = 1; + $this->crop_class = 'g-thumbtype-sqr'; + break; + case "film": + $this->crop_factor = 3/2; + $this->crop_class = 'g-thumbtype-flm'; + break; + case "photo": + default: + $this->crop_factor = 1; + $this->crop_class = 'g-thumbtype-sqr'; + break; + endswitch; + + $this->_thumb_size_y = floor($this->_thumb_size_x / $this->crop_factor); + } + + public function is_sidebarallowed($align) { + return (($this->sidebarallowed == "any") or ($this->sidebarallowed == $align)); + } + + public function breadcrumb_menu($theme, $parents) { + $content = ""; + + if ($theme->item() && !empty($parents)): + $content .= ''; + endif; + + return $content; + } + + protected function sidebar_menu_item($type, $url, $caption, $css) { + if (!$this->is_sidebarallowed($type)): + return ""; + endif; + + $iscurrent = ($this->sidebarvisible == $type); + $content_menu = '
    • '; + if (!$iscurrent): + $content_menu .= ''; + endif; + $content_menu .= '' . $caption . ''; + if (!$iscurrent): + $content_menu .= ''; + endif; + + return $content_menu . '
    • '; + } + + public function sidebar_menu($url) { + if ($this->sidebarallowed != "any"): + return ""; + endif; + + $content_menu = ($this->sidebar_menu_item("left", $url, "Sidebar Left", "left")); + $content_menu .= ($this->sidebar_menu_item("none", $url, "No Sidebar", "full")); + $content_menu .= ($this->sidebar_menu_item("right", $url, "Sidebar Right", "right")); + return '
        ' . $content_menu . '
      '; + } + + public function add_paginator($position) { + if (($this->photonav_position == "both") or ($this->photonav_position == $position)): + return ($this->paginator()); + else: + return ""; + endif; + } + + public function get_thumb_element($item, $addcontext) { + $item_class = $item->is_album() ? "g-album" : "g-photo"; + + if (($this->sidebarallowed == "none") and ($this->sidebarvisible == "none")): + $item_class .= " g-extra-column"; + endif; + + $content = '
    • '; + $content .= $this->thumb_top($item); + + if (($this->crop_factor == 1) and ($item->thumb_width > $item->thumb_height)): + $_shift = 'style="margin-top: ' . floor(($this->_thumb_size_y - $item->thumb_height) / 2) . 'px;"'; + else: + if (($this->crop_factor > 0) and ($item->thumb_width < $item->thumb_height)): + $_shift = 'style="margin-top: -' . floor(($item->thumb_height - $this->_thumb_size_y) / 2) . 'px;"'; + else: + $_shift = ""; + endif; + endif; + + $content .= '
      crop_class . '">

      '; + if ($this->thumb_descmode == "overlay"): + $content .= ''; + $content .= '' . $this->bb2html(html::purify($item->title), 2) . ''; // html::purify(text::limit_chars($item->title, 44, "…")) + $content .= ''; + endif; + $content .= ''; + if (($item->thumb_height == 0) or ($item->thumb_width == 0)): + $content .= 'No Image'; + else: + $content .= $item->thumb_img(); + endif; + $content .= '

      '; + + if ($this->thumb_descmode == "bottom"): + $content .= ''; + $content .= '' . $this->bb2html(html::purify($item->title), 2) . ''; + $content .= ''; + endif; + + if (($this->is_thumbmeta_visible) and (module::is_active("info"))): + $content .= ''; + endif; + + if ($addcontext): + $_text = $this->context_menu($item, "#g-item-id-{$item->id} .g-thumbnail"); + $content .= (stripos($_text, '
    • '))? $_text : null; + endif; + + $content .= '
    '; + $content .= $this->thumb_bottom($item); + $content .= ''; + + return $content; + } + + // $mode: bit 1 - use mix mode ($mode in [1, 3]), bit 2 - strips bbcode ($mode in [2, 3]) + public function bb2html($text, $mode) { + // Syntax Sample: + // -------------- + // [img]http://elouai.com/images/star.gif[/img] + // [url="http://elouai.com"]eLouai[/url] + // [size="25"]HUGE[/size] + // [color="red"]RED[/color] + // [b]bold[/b] + // [i]italic[/i] + // [u]underline[/u] + // [list][*]item[*]item[*]item[/list] + // [code]value="123";[/code] + // [quote]John said yadda yadda yadda[/quote] + + static $bbcode_mappings = array( + "#\\[b\\](.*?)\\[/b\\]#" => "$1", + "#\\[i\\](.*?)\\[/i\\]#" => "$1", + "#\\[u\\](.*?)\\[/u\\]#" => "$1", + "#\\[s\\](.*?)\\[/s\\]#" => "$1", + "#\\[o\\](.*?)\\[/o\\]#" => "$1", + "#\\[url\\](.*?)\[/url\\]#" => "$1", + "#\\[url=(.*?)\\](.*?)\[/url\\]#" => "$2", + "#\\[mail=(.*?)\\](.*?)\[/mail\\]#" => "$2", + "#\\[img\\](.*?)\\[/img\\]#" => "\"\"", + "#\\[img=(.*?)\\](.*?)\[/img\\]#" => "\"$2\"", + "#\\[quote\\](.*?)\\[/quote\\]#" => "

    $1

    ", + "#\\[code\\](.*?)\\[/code\\]#" => "
    $1
    ", + "#\\[size=([^\\[]*)\\]([^\\[]*)\\[/size\\]#" => "$2", + "#\\[color=([^\\[]*)\\]([^\\[]*)\\[/color\\]#" => "$2", + "#\\[class=([^\\[]*)\\]([^\\[]*)\\[/class\\]#" => "$2", + "#\\[center\\](.*?)\\[/center\\]#" => "
    $1
    ", + "#\\[list\\](.*?)\\[/list\\]#" => "
      $1
    ", + "#\\[ul\\](.*?)\\[/ul\\]#" => "
      $1
    ", + "#\\[li\\](.*?)\\[/li\\]#" => "
  • $1
  • ", + ); + + static $bbcode_strip = '|[[\/\!]*?[^\[\]]*?]|si'; + + // Replace any html brackets with HTML Entities to prevent executing HTML or script + // Don't use strip_tags here because it breaks [url] search by replacing & with amp + if (($mode == 1) or ($mode == 3)): + $newtext = str_replace("<", "<", $text); + $newtext = str_replace(">", ">", $newtext); + $newtext = str_replace(""", "\"", $newtext); + else: + $newtext = str_replace("<", "<", $text); + $newtext = str_replace(">", ">", $newtext); + $newtext = str_replace("&quot;", """, $newtext); + endif; + + // Convert new line chars to html
    tags + $newtext = nl2br($newtext); + + if (strpos($text, "[") !== false): + if (($mode == 2) or ($mode == 3)): + $newtext = preg_replace($bbcode_strip, '', $newtext); + else: + $newtext = preg_replace(array_keys($bbcode_mappings), array_values($bbcode_mappings), $newtext); + endif; + endif; + + return stripslashes($newtext); //stops slashing, useful when pulling from db + } +} + +?> \ No newline at end of file diff --git a/themes/greydragon/theme.info b/themes/greydragon/theme.info index 2deac9b4..cea1d8d0 100644 --- a/themes/greydragon/theme.info +++ b/themes/greydragon/theme.info @@ -1,6 +1,6 @@ name = "Grey Dragon Theme" -description = "A Crisp theme uses on clear grey colors and minimized on JS overhead" -version = 1.5.8 -author = "2009 Serguei Dosyukov" +description = "A Crisp flexible theme with support of Color Packs and minimized on JS overhead" +version = 2.3.1 +author = "2010 Serguei Dosyukov" site = 1 admin = 0 diff --git a/themes/greydragon/thumbnail.png b/themes/greydragon/thumbnail.png index 89cd6bda..4b80ecaf 100644 Binary files a/themes/greydragon/thumbnail.png and b/themes/greydragon/thumbnail.png differ diff --git a/themes/greydragon/views/album.html.php b/themes/greydragon/views/album.html.php index 8164bace..49fa5cf4 100644 --- a/themes/greydragon/views/album.html.php +++ b/themes/greydragon/views/album.html.php @@ -1,51 +1,55 @@ - - -
    - album_top() ?> -

    title) ?>

    -
    description)? bb2html(html::purify($item->description), 1) : null; ?>
    -
    - - -paginator() ?> - - -
      - - $child): ?> - - is_album()): ?> - - - -
    • - thumb_top($child) ?> -

      - thumb_img() ?> -

      - thumb_bottom($child) ?> -

      title) ?>

      - context_menu($child, "#g-item-id-{$child->id} .g-thumbnail") ?> - '))? $_text : null; ?> - - - -
    • - - - admin || access::can("add", $item)): ?> - id") ?> -
    • Add some.", - array("attrs" => html::mark_clean("href=\"$addurl\" class=\"g-dialog-link\""))) ?>
    • - -
    • - - -
    -album_bottom() ?> - - -paginator() ?> - + +
    + album_top() ?> +

    bb2html(html::purify($item->title), 1) ?>

    +
    + +add_paginator("top"); ?> + +photo_descmode == "top") and ($item->description)): ?> +
    bb2html(html::purify($item->description), 1) ?>
    + + +
      + + $child): ?> + get_thumb_element($child, TRUE) ?> + + + admin || access::can("add", $item)): ?> + id") ?> +
    • Add some.", + array("attrs" => html::mark_clean("href=\"$addurl\" class=\"g-dialog-link\""))) ?>
    • + +
    • + + +
    +album_bottom() ?> + +photo_descmode == "bottom") and ($item->description)): ?> +
    bb2html(html::purify($item->description), 1) ?>
    + + +add_paginator("bottom"); ?> diff --git a/themes/greydragon/views/block.html.php b/themes/greydragon/views/block.html.php index 699d7c22..af29546f 100644 --- a/themes/greydragon/views/block.html.php +++ b/themes/greydragon/views/block.html.php @@ -1,10 +1,33 @@ - - - - -
    -

    -
    - -
    -
    + + + + +
    + is_blockheader_visible): ?> +

    + +
    + +
    +
    diff --git a/themes/greydragon/views/dynamic.html.php b/themes/greydragon/views/dynamic.html.php index e136fd53..1f787cf3 100644 --- a/themes/greydragon/views/dynamic.html.php +++ b/themes/greydragon/views/dynamic.html.php @@ -1,35 +1,39 @@ - -
    -
    - dynamic_top() ?> -
    -

    -
    - - -paginator() ?> - - -
      - $child): ?> -
    • "> - thumb_top($child) ?> -

      - photo -

      -

      title) ?>

      - thumb_bottom($child) ?> - -
    • - -
    -dynamic_bottom() ?> - - -paginator() ?> - \ No newline at end of file + +
    +
    + dynamic_top() ?> +
    +

    +
    + +add_paginator("top"); ?> + +
      + $child): ?> + get_thumb_element($child) ?> + +
    +dynamic_bottom() ?> + +add_paginator("bottom"); ?> diff --git a/themes/greydragon/views/info_block.html.php b/themes/greydragon/views/info_block.html.php index 69f6a1e9..d3860584 100644 --- a/themes/greydragon/views/info_block.html.php +++ b/themes/greydragon/views/info_block.html.php @@ -1,9 +1,24 @@ - - - + +
      + owner): ?> +
    • + + owner->url): ?> + owner->display_name()) ?> + + owner->display_name()) ?> + +
    • + + captured): ?> +
    • + + captured)?> +
    • + + description): ?> +
    • + bb2html(html::purify($item->description), 1) ?> +
    • + +
    diff --git a/themes/greydragon/views/movie.html.php b/themes/greydragon/views/movie.html.php index e69de29b..ec870608 100644 --- a/themes/greydragon/views/movie.html.php +++ b/themes/greydragon/views/movie.html.php @@ -0,0 +1,43 @@ + +
    + photo_top() ?> + +
    +

    bb2html(html::purify($item->title), 1) ?>

    +
    bb2html(html::purify($item->description), 1) ?>
    +
    + + add_paginator("top"); ?> + +
    + resize_top($item) ?> + movie_img(array("class" => "g-movie", "id" => "g-movie-id-{$item->id}")); ?> + context_menu($item, "#g-movie-id-{$item->id}") ?> + resize_bottom($item) ?> +
    + + add_paginator("bottom"); ?> + + photo_bottom() ?> +
    diff --git a/themes/greydragon/views/no_sidebar.html.php b/themes/greydragon/views/no_sidebar.html.php index 2239cf91..dd61bb77 100644 --- a/themes/greydragon/views/no_sidebar.html.php +++ b/themes/greydragon/views/no_sidebar.html.php @@ -1,3 +1,24 @@ - - -
     
    + +  ?> + diff --git a/themes/greydragon/views/page.html.php b/themes/greydragon/views/page.html.php index 90a9f09c..13664d65 100644 --- a/themes/greydragon/views/page.html.php +++ b/themes/greydragon/views/page.html.php @@ -1,76 +1,66 @@ - - - - +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +load_sessioninfo(); ?> + + enable_pagecache) and ($theme->item())): + // Page will expire in 60 seconds + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 60).'GMT'); + header("Cache-Control: public"); + header("Cache-Control: post-check=3600, pre-check=43200", false); + header("Content-Type: text/html; charset=UTF-8"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); + endif; ?> - - - <? if ($page_title): ?> +<?= " <title>"; ?> +<? if ($page_title): ?> <?= $page_title ?> <? else: ?> <? if ($theme->item()): ?> <? if ($theme->item()->is_album()): ?> -<?= t("Browse Album :: %album_title", array("album_title" => $theme->item()->title)) ?> +<?= t("Browse Album :: %album_title", array("album_title" => $theme->bb2html($theme->item()->title, 2))) ?> <? elseif ($theme->item()->is_photo()): ?> -<?= t("Photo :: %photo_title", array("photo_title" => $theme->item()->title)) ?> +<?= t("Photo :: %photo_title", array("photo_title" => $theme->bb2html($theme->item()->title, 2))) ?> <? else: ?> -<?= t("Movie :: %movie_title", array("movie_title" => $theme->item()->title)) ?> +<?= t("Movie :: %movie_title", array("movie_title" => $theme->bb2html($theme->item()->title, 2))) ?> <? endif ?> <? elseif ($theme->tag()): ?> -<?= t("Browse Tag :: %tag_title", array("tag_title" => $theme->tag()->name)) ?> +<?= t("Browse Tag :: %tag_title", array("tag_title" => $theme->bb2html($theme->tag()->name, 2))) ?> <? else: /* Not an item, not a tag, no page_title specified. Help! */ ?> <?= t("Gallery") ?> <? endif ?> <? endif ?> - - - - - - " type="image/x-icon" /> - - script("jquery.js") ?> - script("jquery.form.js") ?> - script("jquery-ui.js") ?> - +disable_seosupport): ?> + ' . "\n"; ?> + ' . "\n"; ?> + ' . "\n"; ?> + ' . "\n"; ?> + ' . "\n"; ?> + +" type="image/x-icon" /> +script("jquery.js") ?> +script("jquery.form.js") ?> +script("jquery-ui.js") ?> +page_subtype == "movie"): ?> +script("flowplayer.js") ?> + +script("gallery.ajax.js") ?> head() ?> +" type="text/css" media="screen,print,projection" /> +color_pack . "/colors.css") ?>" type="text/css" media="screen,print,projection" /> - " type="text/css" media="screen,print,projection" /> - " type="text/css" media="screen,print,projection" /> + + - - - - + page_top() ?> @@ -80,56 +70,30 @@ -guest): ?> -
    - site_menu("") ?> +guest) or ($theme->show_guest_menu)): ?> +
    "> + site_menu() ?>
    messages() ?> header_bottom() ?> - - + +loginmenu_position == "header"): ?> + user_menu() ?> + +show_breadcrumbs): ?> + breadcrumb_menu($theme, $parents); ?> +
    - -
      - - - -
    • '; ?>">Sidebar Left"; ?>
    • - - - -
    • '; ?>">No Sidebar"; ?>
    • - - - -
    • '; ?>">Sidebar Right"; ?>
    • - -
    - - -
    + sidebar_menu($url) ?> +
    "> album_menu() ?> @@ -141,30 +105,29 @@
    - -' ?> - - -' ?> - + sidebarvisible=="left"): ?> + ' ?> + sidebarvisible=="none"): ?> + + ' ?> + -page_subtype != "login") && ($sidebarvisible != "none")): ?> - - -" : null ?> + page_subtype != "login") and ($theme->page_subtype != "reauthenticate") and ($theme->sidebarvisible != "none")): ?> + + + sidebarvisible != "none")? "
    " : null ?> - -' ?> - -' ?> - -' ?> - + sidebarvisible == "left"): ?> + ' ?> + sidebarvisible == "none"): ?> + ' ?> + + ' ?> + -
    +
    - page_bottom() ?> - - -// -// <bgsound src="/music/collection.m3u"> -// -// -// -// -?> diff --git a/themes/greydragon/views/pager_photo.html.php b/themes/greydragon/views/pager_photo.html.php deleted file mode 100644 index 59326ae5..00000000 --- a/themes/greydragon/views/pager_photo.html.php +++ /dev/null @@ -1,106 +0,0 @@ - - -parent()->children(); - $pagination_msg = t("Photo:") . ' '; - if ($sibling_count < 13) { - - for ($i = 1; $i <= $sibling_count; $i++) { - if ($i == $position) { - $pagination_msg .= '' . t($i) . ''; - } else { - - $pagination_msg .= '' . t($i) . ''; - } - if ($i < $sibling_count) { $pagination_msg .= '·'; }; - } - - } elseif ($position < 9) { - - for ($i = 1; $i <= 10; $i++) { - if ($i == $position) { - $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < 10) { $pagination_msg .= '·'; }; - } - - $pagination_msg .= '…'; - $pagination_msg .= '' . t($sibling_count - 1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t($sibling_count) . ''; - - } elseif ($position > $sibling_count - 8) { - - $pagination_msg .= '' . t(1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; - $pagination_msg .= '…'; - - for ($i = $sibling_count - 9; $i <= $sibling_count; $i++) { - if ($i == $position) { - $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < $sibling_count) { $pagination_msg .= '·'; }; - } - - } else { - - $pagination_msg .= '' . t(1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; - $pagination_msg .= '…'; - - for ($i = $position - 5; $i <= $position + 5; $i++) { - if ($i == $position) { - $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < $position + 5) { $pagination_msg .= '·'; }; - } - - $pagination_msg .= '…'; - $pagination_msg .= '' . t($sibling_count - 1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t($sibling_count) . ''; - } - } -?> - -
      -
    • -
    • - 1): ?> - ">  - -   - - - - ">  - -   - - - - ">  - -   - - - 1) && ($sibling_count > $position)): ?> - ">  - -   - -
    • -
    \ No newline at end of file diff --git a/themes/greydragon/views/paginator.html.php b/themes/greydragon/views/paginator.html.php index e5b2460a..9b5e725e 100644 --- a/themes/greydragon/views/paginator.html.php +++ b/themes/greydragon/views/paginator.html.php @@ -1,4 +1,25 @@ - + parent(); + endif; $current_page = $page; - $total_pages = $max_pages; + $total_pages = $max_pages; // Prepare page url list - for ($i = 1; $i <= $total_pages; $i++) { + for ($i = 1; $i <= $total_pages; $i++): $_pagelist[$i] = url::site(url::merge(array("page" => $i))); - } + endfor; break; case "item": + if ($item): + $parent = $item->parent(); + endif; $current_page = $position; $total_pages = $total; $siblings = $item->parent()->children(); - for ($i = 1; $i <= $total; $i++) { + for ($i = 1; $i <= $total; $i++): $_pagelist[$i] = $siblings[$i-1]->url(); - } + endfor; break; default: $current_page = 1; @@ -55,98 +78,109 @@ break; } - if ($total_pages <= 1) { + if ($total_pages <= 1): $pagination_msg = " "; - } else { + else: $pagination_msg = t("Page:") . ' '; - if ($total_pages < 13) { - for ($i = 1; $i <= $total_pages; $i++) { - if ($i == $current_page) { + if ($total_pages < 13): + for ($i = 1; $i <= $total_pages; $i++): + if ($i == $current_page): $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < $total_pages) { $pagination_msg .= '·'; }; - } - } elseif ($current_page < 9) { - for ($i = 1; $i <= 10; $i++) { - if ($i == $current_page) { + else: + $pagination_msg .= '' . t($i) . ''; + endif; + if ($i < $total_pages): + $pagination_msg .= '·'; + endif; + endfor; + elseif ($current_page < 9): + for ($i = 1; $i <= 10; $i++): + if ($i == $current_page): $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < 10) { $pagination_msg .= '·'; }; - } + else: + $pagination_msg .= '' . t($i) . ''; + endif; + if ($i < 10): + $pagination_msg .= '·'; + endif; + endfor; $pagination_msg .= '…'; - $pagination_msg .= '' . t($total_pages - 1) . ''; + $pagination_msg .= '' . t($total_pages - 1) . ''; $pagination_msg .= '·'; - $pagination_msg .= '' . t($total_pages) . ''; + $pagination_msg .= '' . t($total_pages) . ''; - } elseif ($current_page > $total_pages - 8) { - - $pagination_msg .= '' . t(1) . ''; + elseif ($current_page > $total_pages - 8): + $pagination_msg .= '' . t(1) . ''; $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; + $pagination_msg .= '' . t(2) . ''; $pagination_msg .= '…'; - for ($i = $total_pages - 9; $i <= $total_pages; $i++) { - if ($i == $current_page) { + for ($i = $total_pages - 9; $i <= $total_pages; $i++): + if ($i == $current_page): $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < $total_pages) { $pagination_msg .= '·'; }; - } + else: + $pagination_msg .= '' . t($i) . ''; + endif; + if ($i < $total_pages): + $pagination_msg .= '·'; + endif; + endfor; - } else { - - $pagination_msg .= '' . t(1) . ''; + else: + $pagination_msg .= '' . t(1) . ''; $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; + $pagination_msg .= '' . t(2) . ''; $pagination_msg .= '…'; - for ($i = $current_page - 5; $i <= $current_page + 5; $i++) { - if ($i == $current_page) { + for ($i = $current_page - 5; $i <= $current_page + 5; $i++): + if ($i == $current_page): $pagination_msg .= '' . t($i) . ''; - } else { - $pagination_msg .= '' . t($i) . ''; - } - if ($i < $current_page + 5) { $pagination_msg .= '·'; }; - } + else: + $pagination_msg .= '' . t($i) . ''; + endif; + if ($i < $current_page + 5): + $pagination_msg .= '·'; + endif; + endfor; $pagination_msg .= '…'; - $pagination_msg .= '' . t($total_pages - 1) . ''; + $pagination_msg .= '' . t($total_pages - 1) . ''; $pagination_msg .= '·'; - $pagination_msg .= '' . t($total_pages) . ''; - } - } + $pagination_msg .= '' . t($total_pages) . ''; + endif; + endif; ?> '; ?> + + +
    + bb2html(html::purify($item->title), 1); ?> +
    +

    +
    + add_paginator("top"); ?> + photo_top() ?> + photo_descmode == "top") and ($_description)): ?> +
    + +
    + resize_top($item) ?> + + file_url() . '" class="g-sb-preview" '; ?> + + + + resize_width; ?> + parent()->children(); ?> + +
    + rand_key != $item->rand_key)); $i++): + ?> + "> + resize_img(array("id" => "g-photo-id-{$item->id}", "class" => "g-resize", "alt" => $_title)) ?> + + + photo_descmode == "overlay") and ($_description)): ?> + More + + + + + +
    + resize_bottom($item) ?> +
    + photo_descmode == "bottom") and ($_description)): ?> +
    + + add_paginator("bottom"); ?> + photo_bottom() ?> +
    diff --git a/themes/greydragon/views/search.html.php b/themes/greydragon/views/search.html.php index d8045c7d..94fc170c 100644 --- a/themes/greydragon/views/search.html.php +++ b/themes/greydragon/views/search.html.php @@ -1,37 +1,43 @@ - - +

    $q)) ?>

    - -paginator() ?> - - + add_paginator("top"); ?>
      - - is_album()): ?> - - -
    • -

      - thumb_img() ?> -

      -

      title) ?>

      -
    • + is_album() ? "g-album" : "g-photo" ?> + "> ?> + get_thumb_element($item) ?> + ?>
    - - -paginator() ?> - - + add_paginator("bottom"); ?>

     

    %term", array("term" => $q)) ?>

    -
    - + \ No newline at end of file diff --git a/themes/greydragon/views/sidebar.html.php b/themes/greydragon/views/sidebar.html.php index da75de54..0cad333d 100644 --- a/themes/greydragon/views/sidebar.html.php +++ b/themes/greydragon/views/sidebar.html.php @@ -1,5 +1,8 @@ - -sidebar_top() ?> -
     
    -sidebar_blocks() ?> -sidebar_bottom() ?> + + +sidebar_top() ?> +
     
    +page_subtype == "album") or ($theme->page_subtype == "photo") or ($theme->page_subtype == "movie") or ($theme->item())): ?> +sidebar_blocks() ?> + +sidebar_bottom() ?> diff --git a/themes/greydragon/views/support/bbtohtml.php b/themes/greydragon/views/support/bbtohtml.php deleted file mode 100644 index c18faf4e..00000000 --- a/themes/greydragon/views/support/bbtohtml.php +++ /dev/null @@ -1,63 +0,0 @@ - "$1", - "#\\[i\\](.*?)\\[/i\\]#" => "$1", - "#\\[u\\](.*?)\\[/u\\]#" => "$1", - "#\\[s\\](.*?)\\[/s\\]#" => "$1", - "#\\[o\\](.*?)\\[/o\\]#" => "$1", - "#\\[url\\](.*?)\[/url\\]#" => "$1", - "#\\[url=(.*?)\\](.*?)\[/url\\]#" => "$2", - "#\\[mail=(.*?)\\](.*?)\[/mail\\]#" => "$2", - "#\\[img\\](.*?)\\[/img\\]#" => "\"\"", - "#\\[img=(.*?)\\](.*?)\[/img\\]#" => "\"$2\"", - "#\\[quote\\](.*?)\\[/quote\\]#" => "

    $1

    ", - "#\\[code\\](.*?)\\[/code\\]#" => "
    $1
    ", - "#\\[size=([^\\[]*)\\]([^\\[]*)\\[/size\\]#" => "$2", - "#\\[color=([^\\[]*)\\]([^\\[]*)\\[/color\\]#" => "$2", - "#\\[class=([^\\[]*)\\]([^\\[]*)\\[/class\\]#" => "$2", - "#\\[center\\](.*?)\\[/center\\]#" => "
    $1
    ", - "#\\[list\\](.*?)\\[/list\\]#" => "
      $1
    ", - "#\\[ul\\](.*?)\\[/ul\\]#" => "
      $1
    ", - "#\\[li\\](.*?)\\[/li\\]#" => "
  • $1
  • ", - ); - - // Replace any html brackets with HTML Entities to prevent executing HTML or script - // Don't use strip_tags here because it breaks [url] search by replacing & with amp - if ($mixmode == 1) - { - $newtext = str_replace("<", "<", $text); - $newtext = str_replace(">", ">", $newtext); - $newtext = str_replace(""", "\"", $newtext); - } else { - $newtext = str_replace("<", "<", $text); - $newtext = str_replace(">", ">", $newtext); - $newtext = str_replace("&quot;", """, $newtext); - } - - // Convert new line chars to html
    tags - $newtext = nl2br($newtext); - - if (strpos($text, "[") !== false) { - $newtext = preg_replace(array_keys($bbcode_mappings), array_values($bbcode_mappings), $newtext); - } - - return stripslashes($newtext); //stops slashing, useful when pulling from db -} - -?> \ No newline at end of file diff --git a/themes/greydragon/views/support/pagination.php b/themes/greydragon/views/support/pagination.php deleted file mode 100644 index e69de29b..00000000 diff --git a/themes/greydragon/views/tag_block.html.php b/themes/greydragon/views/tag_block.html.php index 6cb8acb9..f9bc5886 100644 --- a/themes/greydragon/views/tag_block.html.php +++ b/themes/greydragon/views/tag_block.html.php @@ -6,9 +6,19 @@ url, { max: 30, multiple: true, - multipleSeparator: ',', - cacheLength: 1} + multipleSeparator: ',', + cacheLength: 1} ); + + $("#g-add-tag-form").ajaxForm({ + dataType: "json", + success: function(data) { + if (data.result == "success") { + $("#g-tag-cloud").html(data.cloud); + } + $("#g-add-tag-form").resetForm(); + } + }); });
    "> diff --git a/themes/greydragon/views/user_profile.html.php b/themes/greydragon/views/user_profile.html.php new file mode 100644 index 00000000..b7d92f40 --- /dev/null +++ b/themes/greydragon/views/user_profile.html.php @@ -0,0 +1,50 @@ + + + +
    +

    $user->display_name())) ?>

    + + + + " + alt="display_name()) ?>" + class="g-avatar g-left" width="40" height="40" /> + + + +
    +

    title) ?>

    +
    + view ?> +
    +
    + +