From 4fb1704f9a5b151bf66ea44df0bbd7d8b82176b2 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Mon, 5 Jul 2010 13:53:09 +0200 Subject: [PATCH 001/300] Merge the updates from wind --- themes/sobriety/js/ui.init.js | 3 +++ themes/sobriety/theme.info | 4 ++-- themes/sobriety/views/album.html.php | 2 +- themes/sobriety/views/form_uploadify.html.php | 4 ++-- themes/sobriety/views/page.html.php | 6 ++++-- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/themes/sobriety/js/ui.init.js b/themes/sobriety/js/ui.init.js index 2e1e6e89..4393a04a 100644 --- a/themes/sobriety/js/ui.init.js +++ b/themes/sobriety/js/ui.init.js @@ -43,6 +43,9 @@ $(document).ready(function() { }); } + // Remove titles for menu options since we're displaying that text anyway + $(".sf-menu a, .sf-menu li").removeAttr("title"); + // Album and search results views /*if ($("#g-album-grid").length) { // Set equal height for album items and vertically align thumbnails/metadata diff --git a/themes/sobriety/theme.info b/themes/sobriety/theme.info index f2bcb5f3..8ac699d6 100644 --- a/themes/sobriety/theme.info +++ b/themes/sobriety/theme.info @@ -4,5 +4,5 @@ version = 1 author = "Romain LE DISEZ" site = 1 admin = 0 -;wind commit = 2bbce8dddb0ab0a00aee727e2f639b793988a1d1 -;wind date = Thu Jun 17 09:10:01 2010 -0700 +;wind commit = c41bd46a744479813b3442f3e20ce17765353e70 +;wind date = Wed Jun 23 20:42:42 2010 +0800 diff --git a/themes/sobriety/views/album.html.php b/themes/sobriety/views/album.html.php index eabe07c3..b9072e2b 100644 --- a/themes/sobriety/views/album.html.php +++ b/themes/sobriety/views/album.html.php @@ -29,7 +29,7 @@ admin || access::can("add", $item)): ?> - id") ?> + id") ?>
  • Add some.", array("attrs" => html::mark_clean("href=\"$addurl\" class=\"g-dialog-link\""))) ?>
  • diff --git a/themes/sobriety/views/form_uploadify.html.php b/themes/sobriety/views/form_uploadify.html.php index 6012f40b..62171c04 100644 --- a/themes/sobriety/views/form_uploadify.html.php +++ b/themes/sobriety/views/form_uploadify.html.php @@ -49,7 +49,7 @@ uploader: "", script: "id}") ?>", scriptData: , - fileExt: "*.gif;*.jpg;*.jpeg;*.png;*.flv;*.mp4;*.GIF;*.JPG;*.JPEG;*.PNG;*.FLV;*.MP4", + fileExt: "*.gif;*.jpg;*.jpeg;*.png;*.flv;*.mp4;*.m4v;*.GIF;*.JPG;*.JPEG;*.PNG;*.FLV;*.MP4;*.M4V", fileDesc: for_js() ?>, cancelImg: "", simUploadLimit: , @@ -166,4 +166,4 @@ */ -?> \ No newline at end of file +?> diff --git a/themes/sobriety/views/page.html.php b/themes/sobriety/views/page.html.php index 040ec1c5..cf10b85e 100644 --- a/themes/sobriety/views/page.html.php +++ b/themes/sobriety/views/page.html.php @@ -95,11 +95,13 @@ the immediate parent so that when you go back up a level you're on the right page. --> item()->id}" : null) ?>">title) ?> + "show={$theme->item()->id}" : null) ?>">title), 15) ?> -
  • ">item()->title) ?>
  • +
  • "> + item()->title), 15) ?> +
  • From d782c7b8506db56eca764baa2ff766aeac62e9db Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Mon, 5 Jul 2010 13:54:04 +0200 Subject: [PATCH 002/300] Update theme.info --- themes/sobriety/theme.info | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/themes/sobriety/theme.info b/themes/sobriety/theme.info index 8ac699d6..545a0815 100644 --- a/themes/sobriety/theme.info +++ b/themes/sobriety/theme.info @@ -4,5 +4,5 @@ version = 1 author = "Romain LE DISEZ" site = 1 admin = 0 -;wind commit = c41bd46a744479813b3442f3e20ce17765353e70 -;wind date = Wed Jun 23 20:42:42 2010 +0800 +;wind commit = 3b05db2685d92ca538d7993c960b06ea32f3a8df +;wind date = Wed Jun 23 11:16:56 2010 -0700 From 3cd44bf7667e251e1d3c4ed75996f28931f4202b Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Tue, 13 Jul 2010 21:48:19 +0200 Subject: [PATCH 003/300] Initial Commit (v1). downloadalbum is a module that provides a way of downloading a complete album. A ZIP archive is generated on-the-fly in a efficient way: it doesn't burn your CPU nor eat your RAM. There is nothing stored on disk so it will not waste space. The module add a floppy icon on an album when the visitor has the view_full right. This module is based on downloadfullsize. --- .../controllers/downloadalbum.php | 258 ++++++++++++++++++ .../downloadalbum/css/downloadalbum_menu.css | 3 + .../helpers/downloadalbum_event.php | 32 +++ .../helpers/downloadalbum_theme.php | 26 ++ .../images/ico-view-downloadalbum.png | Bin 0 -> 4125 bytes modules/downloadalbum/module.info | 3 + 6 files changed, 322 insertions(+) create mode 100644 modules/downloadalbum/controllers/downloadalbum.php create mode 100644 modules/downloadalbum/css/downloadalbum_menu.css create mode 100644 modules/downloadalbum/helpers/downloadalbum_event.php create mode 100644 modules/downloadalbum/helpers/downloadalbum_theme.php create mode 100644 modules/downloadalbum/images/ico-view-downloadalbum.png create mode 100644 modules/downloadalbum/module.info diff --git a/modules/downloadalbum/controllers/downloadalbum.php b/modules/downloadalbum/controllers/downloadalbum.php new file mode 100644 index 00000000..49cba553 --- /dev/null +++ b/modules/downloadalbum/controllers/downloadalbum.php @@ -0,0 +1,258 @@ +init($id); + $files = $this->getFilesList($album); + + // Calculate ZIP size (look behind for details) + $zipsize = 22; + foreach($files as $f) { + $zipsize += 76 + 2*strlen($f) + filesize($f); + } + + // Send headers + $this->prepareOutput(); + $this->sendHeaders($album->name.'.zip', $zipsize); + + // Generate and send ZIP file + // http://www.pkware.com/documents/casestudies/APPNOTE.TXT (v6.3.2) + $lfh_offset = 0; + $cds = ''; + $cds_offset = 0; + foreach($files as $f) { + $f_namelen = strlen($f); + $f_size = filesize($f); + $f_mtime = $this->unix2dostime(filemtime($f)); + $f_crc32 = hexdec(hash_file('crc32b', $f, false)); + + // Local file header + echo pack('VvvvVVVVvva' . $f_namelen, + 0x04034b50, // local file header signature (4 bytes) + 0x0a, // version needed to extract (2 bytes) => 1.0 + 0x0800, // general purpose bit flag (2 bytes) => UTF-8 + 0x00, // compression method (2 bytes) => store + $f_mtime, // last mod file time and date (4 bytes) + $f_crc32, // crc-32 (4 bytes) + $f_size, // compressed size (4 bytes) + $f_size, // uncompressed size (4 bytes) + $f_namelen, // file name length (2 bytes) + 0, // extra field length (2 bytes) + + $f // file name (variable size) + // extra field (variable size) => n/a + ); + + // File data + readfile($f); + + // Data descriptor (n/a) + + // Central directory structure: File header + $cds .= pack('VvvvvVVVVvvvvvVVa' . $f_namelen, + 0x02014b50, // central file header signature (4 bytes) + 0x14, // version made by (2 bytes) + 0x0a, // version needed to extract (2 bytes) => 1.0 + 0x0800, // general purpose bit flag (2 bytes) => UTF-8 + 0x00, // compression method (2 bytes) => store + $f_mtime, // last mod file time and date (4 bytes) + $f_crc32, // crc-32 (4 bytes) + $f_size, // compressed size (4 bytes) + $f_size, // uncompressed size (4 bytes) + $f_namelen, // file name length (2 bytes) + 0, // extra field length (2 bytes) + 0, // file comment length (2 bytes) + 0, // disk number start (2 bytes) + 0, // internal file attributes (2 bytes) + 0x20, // external file attributes (4 bytes) => (magic?) + $lfh_offset, // relative offset of local header (4 bytes) + + $f // file name (variable size) + // extra field (variable size) => n/a + // file comment (variable size) => n/a + ); + + // Update local file header/central directory structure offset + $cds_offset = $lfh_offset += 30 + $f_namelen + $f_size; + } + + // Archive decryption header (n/a) + // Archive extra data record (n/a) + + // Central directory structure: Digital signature (n/a) + echo $cds; // send Central directory structure + + // Zip64 end of central directory record (n/a) + // Zip64 end of central directory locator (n/a) + + // End of central directory record + $numfile = count($files); + $cds_len = strlen($cds); + echo pack('VvvvvVVv', + 0x06054b50, // end of central dir signature (4 bytes) + 0, // number of this disk (2 bytes) + 0, // number of the disk with the start of + // the central directory (2 bytes) + $numfile, // total number of entries in the + // central directory on this disk (2 bytes) + $numfile, // total number of entries in the + // central directory (2 bytes) + $cds_len, // size of the central directory (4 bytes) + $cds_offset, // offset of start of central directory + // with respect to the + // starting disk number (4 bytes) + 0 // .ZIP file comment length (2 bytes) + // .ZIP file comment (variable size) + ); + } + + + /** + * Init + */ + private function init($id) { + $item = ORM::factory("item", $id); + + // Only send an album + if (!$item->is_album()) { + // @todo: throw an exception? + Kohana::log('error', 'item is not an album: '.$item->relative_path()); + exit; + } + + // Must have view_full to download the originals files + access::required("view_full", $item); + + return $item; + } + + /** + * Return the files that must be included in the archive. + */ + private function getFilesList($album) { + $files = array(); + + // Go to the parent of album so the ZIP will not contains all the + // server hierarchy + if (!chdir($album->file_path().'/../')) { + // @todo: throw an exception? + Kohana::log('error', 'unable to chdir('.$item->file_path().'/../)'); + exit; + } + $cwd = getcwd(); + + $items = $album->viewable() + ->descendants(null, null, array(array("type", "<>", "album"))); + foreach($items as $i) { + if (!access::can('view_full', $i)) { + continue; + } + + $relative_path = str_replace($cwd.'/', '', realpath($i->file_path())); + if (!is_readable($relative_path)) { + continue; + } + + $files[] = $relative_path; + } + + if (count($files) === 0) { + // @todo: throw an exception? + Kohana::log('error', 'no zippable files in ['.$album->relative_path().']'); + exit; + } + + return $files; + } + + + /** + * See system/helpers/download.php + */ + private function prepareOutput() { + // Close output buffers + Kohana::close_buffers(FALSE); + // Clear any output + Event::add('system.display', create_function('', 'Kohana::$output = "";')); + } + + /** + * See system/helpers/download.php + */ + private function sendHeaders($filename, $filesize = null) { + if (!is_null($filesize)) { + header('Content-Length: '.$filesize); + } + + // Retrieve MIME type by extension + $mime = Kohana::config('mimes.'.strtolower(substr(strrchr($filename, '.'), 1))); + $mime = empty($mime) ? 'application/octet-stream' : $mime[0]; + header("Content-Type: $mime"); + header('Content-Transfer-Encoding: binary'); + + // Send headers necessary to invoke a "Save As" dialog + header('Content-Disposition: attachment; filename="'.$filename.'"'); + + // Prevent caching + header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); + + if (request::user_agent('browser') === 'Internet Explorer' + AND request::user_agent('version') <= '6.0') + { + // HTTP 1.0 + header('Pragma:'); + + // HTTP 1.1 with IE extensions + header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + } + else + { + // HTTP 1.0 + header('Pragma: no-cache'); + + // HTTP 1.1 + header('Cache-Control: no-cache, max-age=0'); + } + } + + /** + * @return integer DOS date and time + * @param integer _timestamp Unix timestamp + * @desc returns DOS date and time of the timestamp + */ + private function unix2dostime($timestamp) + { + $timebit = getdate($timestamp); + + if ($timebit['year'] < 1980) { + return (1 << 21 | 1 << 16); + } + + $timebit['year'] -= 1980; + + return ($timebit['year'] << 25 | $timebit['mon'] << 21 | + $timebit['mday'] << 16 | $timebit['hours'] << 11 | + $timebit['minutes'] << 5 | $timebit['seconds'] >> 1); + } +} diff --git a/modules/downloadalbum/css/downloadalbum_menu.css b/modules/downloadalbum/css/downloadalbum_menu.css new file mode 100644 index 00000000..e3c4c67e --- /dev/null +++ b/modules/downloadalbum/css/downloadalbum_menu.css @@ -0,0 +1,3 @@ +#g-view-menu #g-download-album-link { + background-image: url('../images/ico-view-downloadalbum.png'); +} diff --git a/modules/downloadalbum/helpers/downloadalbum_event.php b/modules/downloadalbum/helpers/downloadalbum_event.php new file mode 100644 index 00000000..e2fe4420 --- /dev/null +++ b/modules/downloadalbum/helpers/downloadalbum_event.php @@ -0,0 +1,32 @@ +item)) { + $downloadLink = url::site("downloadalbum/zip/{$theme->item->id}"); + $menu + ->append(Menu::factory("link") + ->id("downloadalbum") + ->label(t("Download Album")) + ->url($downloadLink) + ->css_id("g-download-album-link")); + } + } +} diff --git a/modules/downloadalbum/helpers/downloadalbum_theme.php b/modules/downloadalbum/helpers/downloadalbum_theme.php new file mode 100644 index 00000000..2fd23552 --- /dev/null +++ b/modules/downloadalbum/helpers/downloadalbum_theme.php @@ -0,0 +1,26 @@ +item && access::can("view_full", $theme->item)) { + $theme->css("downloadalbum_menu.css"); + } + } +} diff --git a/modules/downloadalbum/images/ico-view-downloadalbum.png b/modules/downloadalbum/images/ico-view-downloadalbum.png new file mode 100644 index 0000000000000000000000000000000000000000..ce7804d2b8a25f1d32606ce151a35bfa0aea8b00 GIT binary patch literal 4125 zcmeHKX;c&E8V=~8NVWCob;I7?aVlNzF_|QUNK)dKAdO8hhFIyT=Uyhs0FfjUlA#GG z6rn00+SW?dY8SUktrbMJvIN-_!lIT%3_G%heF^*0302zObM86vqd(fich1Z=-@MQF zF3zKV_b%H9c5BOXESB1w>+gdqjDZn1OGF>wb91cL#cLax*>bu4h! zFBAW^{Dwea7J~V)z+aQ{FIn;}Os zk>bpxP?;2G;OPLj*a0qTsW(%|+4(dazG8vC7^Y%EkXEZDX&p(Z+6SUA7z~I^g{V{_ zjv#6Rl^CoeDmAZJT>R>TgJ>jbnF^DkO2Fb17NY?e3j}dH@T?YvYRKalAm+0$uZBU|!$_ zbuG{}F9zlXUQpLxi!Qt0+FC@3_p(~NX`MPA&Lt2Q5d=JuYh`^`-{7c~^`ckapzl{k zu{;mk*e>lK8k?D!88nZ{kEaa{k1M0o{Gxsu8Jid#pLF;C?m%Q}@4!f5dCU03rR)uib5~vQjYyrGntpq4us{?0;ph74>FIqzal#L8>cm6s9q6B4Go;+D4LO&SRnkazSAU!M^O_CNR|yX<+h_c@gFqY{8S{NYj&b=KF!Iv9-ChS*{qwC&TldHOP}0^l zSX$9^Gpnk-d&qjxV(x+Sre1Sec9B35ykXPZS5qrGd(DY?HShU`FI{2(uHu`p3wejn z=l+YhJ>bY!(PwX*kH25`xZz=SM{<5mb9?X0t2f9)&M`$nX$5sDdV`y%|J|RPZ)X}h zdd$}|Dm{>omMmN8!jrP$69>;^XXci4_m4zhDe%&qq`3KQ%rkTS!rxq{{{m) za^ZGHLD7Rpmy^r%c_pSEb5dquN4ME#$xE9Uyp%hJg!F>4nzkGIs+a!pDj&U=kz2eI zIlcPTe`n>Fp14|w3#`1nyrQC_va<5gqeoR$RgWJ(HX4o9)zvjMHMO<1b#-<1_4N%6 z4ULVBO-)Tto;+!8ZfDz7Yinz7Z|~^nFqur9ot<4>UEST?Jv}|Wy}f;Xef|CY z0|NttgM&juLuRviczAeZWMp)7bZl&Fe0+RjVq$V~a%yS{S0pV~Q4f<;m9PMst)SdnP z$DPF&anIdQjJI+KngFBa8cc_XX-?36Ugy>($w|%ql z0lhLij>9Xe6&~Bp Date: Tue, 13 Jul 2010 23:32:18 +0200 Subject: [PATCH 004/300] Make the module compatible with RC2, fix a bug (bad method name) --- modules/user_homes/helpers/user_homes_event.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/user_homes/helpers/user_homes_event.php b/modules/user_homes/helpers/user_homes_event.php index 7cb0a487..65077809 100644 --- a/modules/user_homes/helpers/user_homes_event.php +++ b/modules/user_homes/helpers/user_homes_event.php @@ -178,13 +178,13 @@ class user_homes_event_Core { } } - static function album_add_form($form){ + static function album_add_form($parent, $form){ $group = $form->group("privacy") ->label(t("album privacy settings")); $group->checkbox("private")->label(t("Private"))->id("uh_private")->onClick("pc()"); $group->input("username")->label(t("Username"))->id("uh_username") - ->callback("user_homes_event::user_already_exists") + ->callback("user_homes_event::valid_name") ->error_messages("in_use", t("There is already a user with that username")) ->error_messages("required", t("You must enter a username"))->callback("user_homes_event::valid_name")->rules("length[1,32]"); $group->password("password")->label(t("Password"))->id("uh_password") From 7d07ac6d9d00de85111e95fb8ed1566588747de4 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Thu, 15 Jul 2010 17:24:59 +0200 Subject: [PATCH 005/300] Fix the problem of filename with special char on Linux --- modules/downloadalbum/controllers/downloadalbum.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/downloadalbum/controllers/downloadalbum.php b/modules/downloadalbum/controllers/downloadalbum.php index 49cba553..83f4ab0c 100644 --- a/modules/downloadalbum/controllers/downloadalbum.php +++ b/modules/downloadalbum/controllers/downloadalbum.php @@ -72,7 +72,7 @@ class downloadalbum_Controller extends Controller { // Central directory structure: File header $cds .= pack('VvvvvVVVVvvvvvVVa' . $f_namelen, 0x02014b50, // central file header signature (4 bytes) - 0x14, // version made by (2 bytes) + 0x031e, // version made by (2 bytes) => v3 / Unix 0x0a, // version needed to extract (2 bytes) => 1.0 0x0800, // general purpose bit flag (2 bytes) => UTF-8 0x00, // compression method (2 bytes) => store @@ -85,7 +85,7 @@ class downloadalbum_Controller extends Controller { 0, // file comment length (2 bytes) 0, // disk number start (2 bytes) 0, // internal file attributes (2 bytes) - 0x20, // external file attributes (4 bytes) => (magic?) + 0x81b40000, // external file attributes (4 bytes) => chmod 664 $lfh_offset, // relative offset of local header (4 bytes) $f // file name (variable size) From 62df872def6b0938e6b8fafe026e4ff59d232b3c Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Thu, 22 Jul 2010 21:49:14 +0200 Subject: [PATCH 006/300] Workaround for PHP bug 45028 --- modules/downloadalbum/controllers/downloadalbum.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/downloadalbum/controllers/downloadalbum.php b/modules/downloadalbum/controllers/downloadalbum.php index 83f4ab0c..1cbc410c 100644 --- a/modules/downloadalbum/controllers/downloadalbum.php +++ b/modules/downloadalbum/controllers/downloadalbum.php @@ -45,7 +45,7 @@ class downloadalbum_Controller extends Controller { $f_namelen = strlen($f); $f_size = filesize($f); $f_mtime = $this->unix2dostime(filemtime($f)); - $f_crc32 = hexdec(hash_file('crc32b', $f, false)); + $f_crc32 = $this->fixBug45028(hexdec(hash_file('crc32b', $f, false))); // Local file header echo pack('VvvvVVVVvva' . $f_namelen, @@ -255,4 +255,14 @@ class downloadalbum_Controller extends Controller { $timebit['mday'] << 16 | $timebit['hours'] << 11 | $timebit['minutes'] << 5 | $timebit['seconds'] >> 1); } + + /** + * See http://bugs.php.net/bug.php?id=45028 + */ + private function fixBug45028($hash) { + return (version_compare(PHP_VERSION, '5.2.7', '<')) + ? (($hash & 0x000000ff) << 24) + (($hash & 0x0000ff00) << 8) + + (($hash & 0x00ff0000) >> 8) + (($hash & 0xff000000) >> 24) + : $hash; + } } From 687cdfe3daebc18b791ed2cba0a5587d153d2b00 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Thu, 22 Jul 2010 22:59:07 +0200 Subject: [PATCH 007/300] Workaround for a MSIE bug (see http://support.microsoft.com/kb/323308/en-us) --- .../controllers/downloadalbum.php | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/modules/downloadalbum/controllers/downloadalbum.php b/modules/downloadalbum/controllers/downloadalbum.php index 1cbc410c..c2f939f7 100644 --- a/modules/downloadalbum/controllers/downloadalbum.php +++ b/modules/downloadalbum/controllers/downloadalbum.php @@ -217,23 +217,27 @@ class downloadalbum_Controller extends Controller { // Prevent caching header('Expires: Thu, 01 Jan 1970 00:00:00 GMT'); + $pragma = 'no-cache'; + $cachecontrol = 'no-cache, max-age=0'; + + // request::user_agent('browser') seems bugged if (request::user_agent('browser') === 'Internet Explorer' - AND request::user_agent('version') <= '6.0') + || stripos(request::user_agent(), 'msie') !== false + || stripos(request::user_agent(), 'internet explorer') !== false) { - // HTTP 1.0 - header('Pragma:'); + if (request::protocol() === 'https') { + // See http://support.microsoft.com/kb/323308/en-us + $pragma = 'cache'; + $cachecontrol = 'private'; - // HTTP 1.1 with IE extensions - header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); + } else if (request::user_agent('version') <= '6.0') { + $pragma = ''; + $cachecontrol = 'must-revalidate, post-check=0, pre-check=0'; + } } - else - { - // HTTP 1.0 - header('Pragma: no-cache'); - // HTTP 1.1 - header('Cache-Control: no-cache, max-age=0'); - } + header('Pragma: '.$pragma); + header('Cache-Control: '.$cachecontrol); } /** From 2df25e7b743dafa5b9f5f3b960d06cb6c6467d28 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Sep 2010 13:45:49 -0400 Subject: [PATCH 008/300] Initial commit of NoFFMPEG. --- modules/noffmpeg/helpers/movie.php | 137 ++++++++++++++++++ .../noffmpeg/images/missing_movie - Copy.png | Bin 0 -> 8474 bytes modules/noffmpeg/images/missing_movie.png | Bin 0 -> 8228 bytes modules/noffmpeg/module.info | 3 + 4 files changed, 140 insertions(+) create mode 100644 modules/noffmpeg/helpers/movie.php create mode 100644 modules/noffmpeg/images/missing_movie - Copy.png create mode 100644 modules/noffmpeg/images/missing_movie.png create mode 100644 modules/noffmpeg/module.info diff --git a/modules/noffmpeg/helpers/movie.php b/modules/noffmpeg/helpers/movie.php new file mode 100644 index 00000000..801796b8 --- /dev/null +++ b/modules/noffmpeg/helpers/movie.php @@ -0,0 +1,137 @@ +id", "", "post", array("id" => "g-edit-movie-form")); + $form->hidden("from_id")->value($movie->id); + $group = $form->group("edit_item")->label(t("Edit Movie")); + $group->input("title")->label(t("Title"))->value($movie->title) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); + $group->textarea("description")->label(t("Description"))->value($movie->description); + $group->input("name")->label(t("Filename"))->value($movie->name) + ->error_messages( + "conflict", t("There is already a movie, photo or album with this name")) + ->error_messages("no_slashes", t("The movie name can't contain a \"/\"")) + ->error_messages("no_trailing_period", t("The movie name can't end in \".\"")) + ->error_messages("illegal_data_file_extension", t("You cannot change the movie file extension")) + ->error_messages("required", t("You must provide a movie file name")) + ->error_messages("length", t("Your movie file name is too long")); + $group->input("slug")->label(t("Internet Address"))->value($movie->slug) + ->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_edit_form", $movie, $form); + + $group = $form->group("buttons")->label(""); + $group->submit("")->value(t("Modify")); + + return $form; + } + + static function extract_frame($input_file, $output_file) { + $ffmpeg = self::find_ffmpeg(); + if (empty($ffmpeg)) { + // BEGIN rWatcher Edit. + copy(MODPATH . "noffmpeg/images/missing_movie.png", $output_file); + //throw new Exception("@todo MISSING_FFMPEG"); + // END rWatcher Edit. + } + + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . + " -an -ss 00:00:03 -an -r 1 -vframes 1" . + " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; + exec($cmd); + + clearstatcache(); // use $filename parameter when PHP_version is 5.3+ + if (filesize($output_file) == 0) { + // Maybe the movie is shorter, fall back to the first frame. + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . + " -an -an -r 1 -vframes 1" . + " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; + exec($cmd); + + clearstatcache(); + if (filesize($output_file) == 0) { + throw new Exception("@todo FFMPEG_FAILED"); + } + } + } + + static function find_ffmpeg() { + if (!($ffmpeg_path = module::get_var("gallery", "ffmpeg_path")) || !file_exists($ffmpeg_path)) { + $graphics_path = module::get_var("gallery", "graphics_toolkit_path", null); + + putenv("PATH=" . getenv("PATH") . (empty($graphics_path) ? "" : ":$graphics_path") . + ":/usr/local/bin:/opt/local/bin:/opt/bin"); + if (function_exists("exec")) { + $ffmpeg_path = exec("which ffmpeg"); + } + + module::set_var("gallery", "ffmpeg_path", $ffmpeg_path); + } + return $ffmpeg_path; + } + + + /** + * Return the width, height, mime_type and extension of the given movie file. + */ + static function get_file_metadata($file_path) { + $ffmpeg = self::find_ffmpeg(); + if (empty($ffmpeg)) { + // BEGIN rWatcher Edit. + $pi = pathinfo($file_path); + $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. + $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? + "video/mp4" : "video/x-flv"; + return array(320, 240, $mime_type, $extension); + //throw new Exception("@todo MISSING_FFMPEG"); + // END rWatcher Edit. + } + + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($file_path) . " 2>&1"; + $result = `$cmd`; + if (preg_match("/Stream.*?Video:.*?(\d+)x(\d+)/", $result, $regs)) { + list ($width, $height) = array($regs[1], $regs[2]); + } else { + list ($width, $height) = array(0, 0); + } + + $pi = pathinfo($file_path); + $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. + $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? + "video/mp4" : "video/x-flv"; + + return array($width, $height, $mime_type, $extension); + } + +} diff --git a/modules/noffmpeg/images/missing_movie - Copy.png b/modules/noffmpeg/images/missing_movie - Copy.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc97779a64dc9d872d8c882f85f80dd54a99ae5 GIT binary patch literal 8474 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 z zJf5en%$s)w{^QEx#S8v%|3g&VetGDj4?L$9%zK#s@Okqde)!=};Bnvk-uJ%37576Q zxIbk-TTeq$$%^C>8A+8Z)29V_T4h(GBx^=77`Z!yxR5 literal 0 HcmV?d00001 diff --git a/modules/noffmpeg/module.info b/modules/noffmpeg/module.info new file mode 100644 index 00000000..c11f0853 --- /dev/null +++ b/modules/noffmpeg/module.info @@ -0,0 +1,3 @@ +name = "NoFFMPEG" +description = "Allow video uploads on systems without FFMPEG." +version = 1 From 8103c3e4aba741c67bd671327f5d10e8642c3dd6 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 14 Sep 2010 13:49:07 -0400 Subject: [PATCH 009/300] Fixed the thumbnail. --- .../noffmpeg/images/missing_movie - Copy.png | Bin 8474 -> 0 bytes modules/noffmpeg/images/missing_movie.png | Bin 8228 -> 8474 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 modules/noffmpeg/images/missing_movie - Copy.png diff --git a/modules/noffmpeg/images/missing_movie - Copy.png b/modules/noffmpeg/images/missing_movie - Copy.png deleted file mode 100644 index fdc97779a64dc9d872d8c882f85f80dd54a99ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8474 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 v-=8YMvqI>T_g&d4NoiWruzDT;Q*d@L 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 delta 7430 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 Date: Tue, 14 Sep 2010 13:49:07 -0400 Subject: [PATCH 010/300] Fixed the thumbnail. --- .../noffmpeg/images/missing_movie - Copy.png | Bin 8474 -> 0 bytes modules/noffmpeg/images/missing_movie.png | Bin 8228 -> 8474 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 modules/noffmpeg/images/missing_movie - Copy.png diff --git a/modules/noffmpeg/images/missing_movie - Copy.png b/modules/noffmpeg/images/missing_movie - Copy.png deleted file mode 100644 index fdc97779a64dc9d872d8c882f85f80dd54a99ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8474 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 v-=8YMvqI>T_g&d4NoiWruzDT;Q*d@L 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 delta 7430 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 Date: Tue, 14 Sep 2010 13:54:01 -0400 Subject: [PATCH 011/300] I don't even know. --- modules/noffmpeg/helpers/movie.php | 137 ---------------------- modules/noffmpeg/images/missing_movie.png | Bin 8474 -> 0 bytes modules/noffmpeg/module.info | 3 - 3 files changed, 140 deletions(-) delete mode 100644 modules/noffmpeg/helpers/movie.php delete mode 100644 modules/noffmpeg/images/missing_movie.png delete mode 100644 modules/noffmpeg/module.info diff --git a/modules/noffmpeg/helpers/movie.php b/modules/noffmpeg/helpers/movie.php deleted file mode 100644 index 801796b8..00000000 --- a/modules/noffmpeg/helpers/movie.php +++ /dev/null @@ -1,137 +0,0 @@ -id", "", "post", array("id" => "g-edit-movie-form")); - $form->hidden("from_id")->value($movie->id); - $group = $form->group("edit_item")->label(t("Edit Movie")); - $group->input("title")->label(t("Title"))->value($movie->title) - ->error_messages("required", t("You must provide a title")) - ->error_messages("length", t("Your title is too long")); - $group->textarea("description")->label(t("Description"))->value($movie->description); - $group->input("name")->label(t("Filename"))->value($movie->name) - ->error_messages( - "conflict", t("There is already a movie, photo or album with this name")) - ->error_messages("no_slashes", t("The movie name can't contain a \"/\"")) - ->error_messages("no_trailing_period", t("The movie name can't end in \".\"")) - ->error_messages("illegal_data_file_extension", t("You cannot change the movie file extension")) - ->error_messages("required", t("You must provide a movie file name")) - ->error_messages("length", t("Your movie file name is too long")); - $group->input("slug")->label(t("Internet Address"))->value($movie->slug) - ->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_edit_form", $movie, $form); - - $group = $form->group("buttons")->label(""); - $group->submit("")->value(t("Modify")); - - return $form; - } - - static function extract_frame($input_file, $output_file) { - $ffmpeg = self::find_ffmpeg(); - if (empty($ffmpeg)) { - // BEGIN rWatcher Edit. - copy(MODPATH . "noffmpeg/images/missing_movie.png", $output_file); - //throw new Exception("@todo MISSING_FFMPEG"); - // END rWatcher Edit. - } - - $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . - " -an -ss 00:00:03 -an -r 1 -vframes 1" . - " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; - exec($cmd); - - clearstatcache(); // use $filename parameter when PHP_version is 5.3+ - if (filesize($output_file) == 0) { - // Maybe the movie is shorter, fall back to the first frame. - $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . - " -an -an -r 1 -vframes 1" . - " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; - exec($cmd); - - clearstatcache(); - if (filesize($output_file) == 0) { - throw new Exception("@todo FFMPEG_FAILED"); - } - } - } - - static function find_ffmpeg() { - if (!($ffmpeg_path = module::get_var("gallery", "ffmpeg_path")) || !file_exists($ffmpeg_path)) { - $graphics_path = module::get_var("gallery", "graphics_toolkit_path", null); - - putenv("PATH=" . getenv("PATH") . (empty($graphics_path) ? "" : ":$graphics_path") . - ":/usr/local/bin:/opt/local/bin:/opt/bin"); - if (function_exists("exec")) { - $ffmpeg_path = exec("which ffmpeg"); - } - - module::set_var("gallery", "ffmpeg_path", $ffmpeg_path); - } - return $ffmpeg_path; - } - - - /** - * Return the width, height, mime_type and extension of the given movie file. - */ - static function get_file_metadata($file_path) { - $ffmpeg = self::find_ffmpeg(); - if (empty($ffmpeg)) { - // BEGIN rWatcher Edit. - $pi = pathinfo($file_path); - $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. - $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? - "video/mp4" : "video/x-flv"; - return array(320, 240, $mime_type, $extension); - //throw new Exception("@todo MISSING_FFMPEG"); - // END rWatcher Edit. - } - - $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($file_path) . " 2>&1"; - $result = `$cmd`; - if (preg_match("/Stream.*?Video:.*?(\d+)x(\d+)/", $result, $regs)) { - list ($width, $height) = array($regs[1], $regs[2]); - } else { - list ($width, $height) = array(0, 0); - } - - $pi = pathinfo($file_path); - $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. - $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? - "video/mp4" : "video/x-flv"; - - return array($width, $height, $mime_type, $extension); - } - -} diff --git a/modules/noffmpeg/images/missing_movie.png b/modules/noffmpeg/images/missing_movie.png deleted file mode 100644 index fdc97779a64dc9d872d8c882f85f80dd54a99ae5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8474 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 v-=8YMvqI>T_g&d4NoiWruzDT Date: Tue, 14 Sep 2010 14:00:07 -0400 Subject: [PATCH 012/300] Second attempt at commiting NoFFMPEG. --- modules/noffmpeg/helpers/movie.php | 137 ++++++++++++++++++++++ modules/noffmpeg/images/missing_movie.png | Bin 0 -> 8474 bytes modules/noffmpeg/module.info | 3 + 3 files changed, 140 insertions(+) create mode 100644 modules/noffmpeg/helpers/movie.php create mode 100644 modules/noffmpeg/images/missing_movie.png create mode 100644 modules/noffmpeg/module.info diff --git a/modules/noffmpeg/helpers/movie.php b/modules/noffmpeg/helpers/movie.php new file mode 100644 index 00000000..801796b8 --- /dev/null +++ b/modules/noffmpeg/helpers/movie.php @@ -0,0 +1,137 @@ +id", "", "post", array("id" => "g-edit-movie-form")); + $form->hidden("from_id")->value($movie->id); + $group = $form->group("edit_item")->label(t("Edit Movie")); + $group->input("title")->label(t("Title"))->value($movie->title) + ->error_messages("required", t("You must provide a title")) + ->error_messages("length", t("Your title is too long")); + $group->textarea("description")->label(t("Description"))->value($movie->description); + $group->input("name")->label(t("Filename"))->value($movie->name) + ->error_messages( + "conflict", t("There is already a movie, photo or album with this name")) + ->error_messages("no_slashes", t("The movie name can't contain a \"/\"")) + ->error_messages("no_trailing_period", t("The movie name can't end in \".\"")) + ->error_messages("illegal_data_file_extension", t("You cannot change the movie file extension")) + ->error_messages("required", t("You must provide a movie file name")) + ->error_messages("length", t("Your movie file name is too long")); + $group->input("slug")->label(t("Internet Address"))->value($movie->slug) + ->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_edit_form", $movie, $form); + + $group = $form->group("buttons")->label(""); + $group->submit("")->value(t("Modify")); + + return $form; + } + + static function extract_frame($input_file, $output_file) { + $ffmpeg = self::find_ffmpeg(); + if (empty($ffmpeg)) { + // BEGIN rWatcher Edit. + copy(MODPATH . "noffmpeg/images/missing_movie.png", $output_file); + //throw new Exception("@todo MISSING_FFMPEG"); + // END rWatcher Edit. + } + + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . + " -an -ss 00:00:03 -an -r 1 -vframes 1" . + " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; + exec($cmd); + + clearstatcache(); // use $filename parameter when PHP_version is 5.3+ + if (filesize($output_file) == 0) { + // Maybe the movie is shorter, fall back to the first frame. + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($input_file) . + " -an -an -r 1 -vframes 1" . + " -y -f mjpeg " . escapeshellarg($output_file) . " 2>&1"; + exec($cmd); + + clearstatcache(); + if (filesize($output_file) == 0) { + throw new Exception("@todo FFMPEG_FAILED"); + } + } + } + + static function find_ffmpeg() { + if (!($ffmpeg_path = module::get_var("gallery", "ffmpeg_path")) || !file_exists($ffmpeg_path)) { + $graphics_path = module::get_var("gallery", "graphics_toolkit_path", null); + + putenv("PATH=" . getenv("PATH") . (empty($graphics_path) ? "" : ":$graphics_path") . + ":/usr/local/bin:/opt/local/bin:/opt/bin"); + if (function_exists("exec")) { + $ffmpeg_path = exec("which ffmpeg"); + } + + module::set_var("gallery", "ffmpeg_path", $ffmpeg_path); + } + return $ffmpeg_path; + } + + + /** + * Return the width, height, mime_type and extension of the given movie file. + */ + static function get_file_metadata($file_path) { + $ffmpeg = self::find_ffmpeg(); + if (empty($ffmpeg)) { + // BEGIN rWatcher Edit. + $pi = pathinfo($file_path); + $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. + $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? + "video/mp4" : "video/x-flv"; + return array(320, 240, $mime_type, $extension); + //throw new Exception("@todo MISSING_FFMPEG"); + // END rWatcher Edit. + } + + $cmd = escapeshellcmd($ffmpeg) . " -i " . escapeshellarg($file_path) . " 2>&1"; + $result = `$cmd`; + if (preg_match("/Stream.*?Video:.*?(\d+)x(\d+)/", $result, $regs)) { + list ($width, $height) = array($regs[1], $regs[2]); + } else { + list ($width, $height) = array(0, 0); + } + + $pi = pathinfo($file_path); + $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. + $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? + "video/mp4" : "video/x-flv"; + + return array($width, $height, $mime_type, $extension); + } + +} diff --git a/modules/noffmpeg/images/missing_movie.png b/modules/noffmpeg/images/missing_movie.png new file mode 100644 index 0000000000000000000000000000000000000000..fdc97779a64dc9d872d8c882f85f80dd54a99ae5 GIT binary patch literal 8474 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 v-=8YMvqI>T_g&d4NoiWruzDT Date: Thu, 16 Sep 2010 23:32:00 -0400 Subject: [PATCH 013/300] Added a library for determining FLV height and width. --- modules/noffmpeg/helpers/movie.php | 15 +- modules/noffmpeg/libraries/FLVMetaData.php | 194 +++++++++++++++++++++ 2 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 modules/noffmpeg/libraries/FLVMetaData.php diff --git a/modules/noffmpeg/helpers/movie.php b/modules/noffmpeg/helpers/movie.php index 801796b8..40d0940a 100644 --- a/modules/noffmpeg/helpers/movie.php +++ b/modules/noffmpeg/helpers/movie.php @@ -23,6 +23,9 @@ * * Note: by design, this class does not do any permission checking. */ + +include MODPATH . "noffmpeg/libraries/MP4Info.php"; + class movie_Core { static function get_edit_form($movie) { $form = new Forge("movies/update/$movie->id", "", "post", array("id" => "g-edit-movie-form")); @@ -113,7 +116,17 @@ class movie_Core { $extension = isset($pi["extension"]) ? $pi["extension"] : "flv"; // No extension? Assume FLV. $mime_type = in_array(strtolower($extension), array("mp4", "m4v")) ? "video/mp4" : "video/x-flv"; - return array(320, 240, $mime_type, $extension); + $vid_width = 320; + $vid_height = 240; + if (strtolower($extension) == "flv") { + $flvinfo = new FLVMetaData($file_path); + $info = $flvinfo->getMetaData(); + if (($info["width"] != "") && ($info["height"] != "")) { + $vid_width = $info["width"]; + $vid_height = $info["height"]; + } + } + return array($vid_width, $vid_height, $mime_type, $extension); //throw new Exception("@todo MISSING_FFMPEG"); // END rWatcher Edit. } diff --git a/modules/noffmpeg/libraries/FLVMetaData.php b/modules/noffmpeg/libraries/FLVMetaData.php new file mode 100644 index 00000000..bfd8e435 --- /dev/null +++ b/modules/noffmpeg/libraries/FLVMetaData.php @@ -0,0 +1,194 @@ +. + * + * @author Amin Saeedi, + * @copyright Copyright (c) 2009, Amin Saeedi + * @version 1.0 + * + */ +class FLVMetaData { + private $buffer; + private $metaData; + private $fileName; + private $typeFlagsAudio; + private $typeFlagsVideo; + + public $VCidMap = array( + 2=>"Sorenson H.263", + 3=>"Screen Video", + 4=>"VP6", + 5=>"VP6 with Alpha channel", + ); //Video Codec ID(s) + + public $ACidMap = array( + "Linear PCM, platform endian", + "ADPCM", + "MP3", + "Linear PCM, little endian", + "Nellymoser 16-kHz Mono", + "Nellymoser 8-kHz Mono", + "Nellymoser", + "G.711 A-law logarithmic PCM", + "G.711 mu-law logarithmic PCM", + "reserved", + "AAC", + "Speex", + 14=>"MP3 8-Khz", + 15=>"Device-specific sound" + ); //Audio Codec ID(s) + +/** + * CONSTRUCTOR : initialize class members + * + * @param string $flv : flv file path + */ + public function __construct($flv) { + $this->fileName = $flv; + $this->metaData = array( + "duration"=>null, + "size"=>null, + "framerate"=>null, + "width"=>null, + "height"=>null, + "videodatarate"=>null, + "audiodatarate"=>null, + "audiodelay"=>null, + "audiosamplesize"=>null, + "audiosamplerate"=>null, + "audiocodecid"=>null, + "videocodecid"=>null, + "version"=>null, + "headersize"=>0 + ); + } + +/** + * Gets metadata of FLV file + * + * @return array $this->metaData : matadata of FLV + */ + public function getMetaData(){ + if(!file_exists($this->fileName)){ + echo "Error! {$this->fileName} does not exist.
    "; + return false; + } + if(!is_readable($this->fileName)){ + echo "Error! Could not read the file. Check the file permissions.
    "; + return false; + } + $f = @fopen($this->fileName,"rb"); + if(!$f){ + echo "Unknown Error! Could not read the file.
    "; + return; + } + $signature = fread($f,3); + if($signature != "FLV"){ + echo "Error! Wrong file format."; + return false; + } + $this->metaData["version"] = ord(fread($f,1)); + $this->metaData["size"] = filesize($this->fileName); + + $flags = ord(fread($f,1)); + $flags = sprintf("%'04b", $flags); + $this->typeFlagsAudio = substr($flags, 1, 1); + $this->typeFlagsVideo = substr($flags, 3, 1); + + for ($i=0; $i < 4; $i++) { + $this->metaData["headersize"] += ord(fread($f,1)) ; + } + + $this->buffer = fread($f, 400); + fclose($f); + if(strpos($this->buffer, "onMetaData") === false){ + echo "Error! No MetaData Exists."; + return false; + } + + foreach($this->metaData as $k=>$v){ + $this->parseBuffer($k); + } + return $this->metaData; + } + +/** + * Takes a field name of metadata, retrieve it's value and set it in $this->metaData + * + * @param string $fieldName : matadata field name + */ + private function parseBuffer($fieldName){ + $fieldPos = strpos($this->buffer, $fieldName); //get the field position + if($fieldPos !== false){ + $pos = $fieldPos + strlen($fieldName) + 1; + $buffer = substr($this->buffer,$pos); + + $d = ""; + for($i=0; $i < 8;$i++){ + $d .= sprintf("%08b", ord(substr($buffer,$i,1))); + } + + $total = self::bin2Double($d); + $this->metaData[$fieldName] = $total; + } + } + +/** + * Calculates double-precision value of given binary string + * (IEEE Standard 754 - Floating Point Numbers) + * + * @param string binary data $strBin + * @return Float calculated double-precision number + */ + public static function bin2Double($strBin){ + $sb = substr($strBin, 0, 1); // first bit is sign bit + $exponent = substr($strBin, 1, 11); // 11 bits exponent + $fraction = "1".substr($strBin, 12, 52); //52 bits fraction (1.F) + + $s = pow(-1, bindec($sb)); + $dec = pow(2, (bindec($exponent) - 1023)); //Decode exponent + + if($dec == 2047){ + if($fraction == 0){ + if($s==0){ + echo "Infinity"; + }else{ + echo "-Infinity"; + } + }else{ + echo "NaN"; + } + } + + if($dec > 0 && $dec < 2047){ + $t = 1; + for($i=1 ; $i <= 53; $i++){ + $t += ((int)substr($fraction, $i, 1)) * pow(2, -$i); //decode significand + } + $total = $s * $t * $dec ; + return $total; + } + return false; + } +} +?> From 85fbf87b55022a87a4915abe8a4003b6bc90816c Mon Sep 17 00:00:00 2001 From: rWatcher Date: Fri, 24 Sep 2010 00:02:46 -0400 Subject: [PATCH 014/300] Initial commit of videos module. --- modules/videos/controllers/admin_videos.php | 96 ++++++ modules/videos/controllers/videos.php | 298 ++++++++++++++++++ modules/videos/css/videos.css | 38 +++ modules/videos/helpers/videos.php | 49 +++ modules/videos/helpers/videos_event.php | 84 +++++ modules/videos/helpers/videos_installer.php | 52 +++ modules/videos/helpers/videos_theme.php | 55 ++++ modules/videos/js/admin_videos.js | 8 + modules/videos/js/videos.js | 125 ++++++++ modules/videos/models/items_video.php | 22 ++ modules/videos/models/videos_file.php | 21 ++ modules/videos/module.info | 3 + modules/videos/views/admin_videos.html.php | 21 ++ modules/videos/views/movieplayer.html.php | 33 ++ .../videos/views/videos_display_js.html.php | 28 ++ modules/videos/views/videos_tree.html.php | 37 +++ .../videos/views/videos_tree_dialog.html.php | 52 +++ 17 files changed, 1022 insertions(+) create mode 100644 modules/videos/controllers/admin_videos.php create mode 100644 modules/videos/controllers/videos.php create mode 100644 modules/videos/css/videos.css create mode 100644 modules/videos/helpers/videos.php create mode 100644 modules/videos/helpers/videos_event.php create mode 100644 modules/videos/helpers/videos_installer.php create mode 100644 modules/videos/helpers/videos_theme.php create mode 100644 modules/videos/js/admin_videos.js create mode 100644 modules/videos/js/videos.js create mode 100644 modules/videos/models/items_video.php create mode 100644 modules/videos/models/videos_file.php create mode 100644 modules/videos/module.info create mode 100644 modules/videos/views/admin_videos.html.php create mode 100644 modules/videos/views/movieplayer.html.php create mode 100644 modules/videos/views/videos_display_js.html.php create mode 100644 modules/videos/views/videos_tree.html.php create mode 100644 modules/videos/views/videos_tree_dialog.html.php diff --git a/modules/videos/controllers/admin_videos.php b/modules/videos/controllers/admin_videos.php new file mode 100644 index 00000000..5dd8abb9 --- /dev/null +++ b/modules/videos/controllers/admin_videos.php @@ -0,0 +1,96 @@ +page_title = t("Add videos from server"); + $view->content = new View("admin_videos.html"); + $view->content->form = $this->_get_admin_form(); + $paths = unserialize(module::get_var("videos", "authorized_paths", "a:0:{}")); + $view->content->paths = array_keys($paths); + + print $view; + } + + public function add_path() { + access::verify_csrf(); + + $form = $this->_get_admin_form(); + $paths = unserialize(module::get_var("videos", "authorized_paths", "a:0:{}")); + if ($form->validate()) { + if (is_link($form->add_path->path->value)) { + $form->add_path->path->add_error("is_symlink", 1); + } else if (!is_readable($form->add_path->path->value)) { + $form->add_path->path->add_error("not_readable", 1); + } else { + $path = $form->add_path->path->value; + $paths[$path] = 1; + module::set_var("videos", "authorized_paths", serialize($paths)); + message::success(t("Added path %path", array("path" => $path))); + videos::check_config($paths); + url::redirect("admin/videos"); + } + } + + $view = new Admin_View("admin.html"); + $view->content = new View("admin_videos.html"); + $view->content->form = $form; + $view->content->paths = array_keys($paths); + print $view; + } + + public function remove_path() { + access::verify_csrf(); + + $path = Input::instance()->get("path"); + $paths = unserialize(module::get_var("videos", "authorized_paths")); + if (isset($paths[$path])) { + unset($paths[$path]); + message::success(t("Removed path %path", array("path" => $path))); + module::set_var("videos", "authorized_paths", serialize($paths)); + videos::check_config($paths); + } + url::redirect("admin/videos"); + } + + public function autocomplete() { + $directories = array(); + $path_prefix = Input::instance()->get("q"); + foreach (glob("{$path_prefix}*") as $file) { + if (is_dir($file) && !is_link($file)) { + $directories[] = $file; + } + } + + print implode("\n", $directories); + } + + private function _get_admin_form() { + $form = new Forge("admin/videos/add_path", "", "post", + array("id" => "g-server-add-admin-form", "class" => "g-short-form")); + $add_path = $form->group("add_path"); + $add_path->input("path")->label(t("Path"))->rules("required")->id("g-path") + ->error_messages("not_readable", t("This directory is not readable by the webserver")) + ->error_messages("is_symlink", t("Symbolic links are not allowed")); + $add_path->submit("add")->value(t("Add Path")); + + return $form; + } +} diff --git a/modules/videos/controllers/videos.php b/modules/videos/controllers/videos.php new file mode 100644 index 00000000..00ec0691 --- /dev/null +++ b/modules/videos/controllers/videos.php @@ -0,0 +1,298 @@ +item = $item; + $view->tree = new View("videos_tree.html"); + $view->tree->files = $files; + $view->tree->parents = array(); + print $view; + } + + public function children() { + $path = Input::instance()->get("path"); + + $tree = new View("videos_tree.html"); + $tree->files = array(); + $tree->parents = array(); + + // Make a tree with the parents back up to the authorized path, and all the children under the + // current path. + if (videos::is_valid_path($path)) { + $tree->parents[] = $path; + while (videos::is_valid_path(dirname($tree->parents[0]))) { + array_unshift($tree->parents, dirname($tree->parents[0])); + } + + $glob_path = str_replace(array("{", "}", "[", "]"), array("\{", "\}", "\[", "\]"), $path); + foreach (glob("$glob_path/*") as $file) { + if (!is_readable($file)) { + continue; + } + if (!is_dir($file)) { + $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + //if (!in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4", "m4v"))) { + if (!in_array($ext, unserialize(module::get_var("videos", "allowed_extensions")))) { + continue; + } + } + + $tree->files[] = $file; + } + } else { + // Missing or invalid path; print out the list of authorized path + $paths = unserialize(module::get_var("videos", "authorized_paths")); + foreach (array_keys($paths) as $path) { + $tree->files[] = $path; + } + } + print $tree; + } + + /** + * Begin the task of adding files. + */ + public function start() { + access::verify_csrf(); + $item = ORM::factory("item", Input::instance()->get("item_id")); + + foreach (Input::instance()->post("paths") as $path) { + if (videos::is_valid_path($path)) { + $paths[] = array($path, null); + } + } + + $task_def = Task_Definition::factory() + ->callback("Videos_Controller::add") + ->description(t("Add videos from the local server")) + ->name(t("Add from server")); + $task = task::create($task_def, array("item_id" => $item->id, "queue" => $paths)); + + json::reply( + array("result" => "started", + "status" => (string)$task->status, + "url" => url::site("videos/run/$task->id?csrf=" . access::csrf_token()))); + } + + /** + * Run the task of adding files + */ + function run($task_id) { + access::verify_csrf(); + + $task = ORM::factory("task", $task_id); + if (!$task->loaded() || $task->owner_id != identity::active_user()->id) { + access::forbidden(); + } + + $task = task::run($task_id); + // Prevent the JavaScript code from breaking by forcing a period as + // decimal separator for all locales with sprintf("%F", $value). + json::reply(array("done" => (bool)$task->done, + "status" => (string)$task->status, + "percent_complete" => sprintf("%F", $task->percent_complete))); + } + + /** + * This is the task code that adds photos and albums. It first examines all the target files + * and creates a set of Server_Add_File_Models, then runs through the list of models and adds + * them one at a time. + */ + static function add($task) { + $mode = $task->get("mode", "init"); + $start = microtime(true); + + switch ($mode) { + case "init": + $task->set("mode", "build-file-list"); + $task->percent_complete = 0; + $task->status = t("Starting up"); + batch::start(); + break; + + case "build-file-list": // 0% to 10% + // We can't fit an arbitrary number of paths in a task, so store them in a separate table. + // Don't use an iterator here because we can't get enough control over it when we're dealing + // with a deep hierarchy and we don't want to go over our time quota. The queue is in the + // form [path, parent_id] where the parent_id refers to another Server_Add_File_Model. We + // have this extra level of abstraction because we don't know its Item_Model id yet. + $queue = $task->get("queue"); + $paths = unserialize(module::get_var("videos", "authorized_paths")); + + while ($queue && microtime(true) - $start < 0.5) { + list($file, $parent_entry_id) = array_shift($queue); + // Ignore the staging directories as directories to be imported. + if (empty($paths[$file])) { + $entry = ORM::factory("videos_file"); + $entry->task_id = $task->id; + $entry->file = $file; + $entry->parent_id = $parent_entry_id; + $entry->save(); + $entry_id = $entry->id; + } else { + $entry_id = null; + } + + $file = preg_quote($file); + foreach (glob("$file/*") as $child) { + if (is_dir($child)) { + $queue[] = array($child, $entry_id); + } else { + $ext = strtolower(pathinfo($child, PATHINFO_EXTENSION)); + //if (in_array($ext, array("gif", "jpeg", "jpg", "png", "flv", "mp4", "m4v")) && + if (in_array($ext, unserialize(module::get_var("videos", "allowed_extensions"))) && + filesize($child) > 0) { + $child_entry = ORM::factory("videos_file"); + $child_entry->task_id = $task->id; + $child_entry->file = $child; + $child_entry->parent_id = $entry_id; + $child_entry->save(); + } + } + } + } + + // We have no idea how long this can take because we have no idea how deep the tree + // hierarchy rabbit hole goes. Leave ourselves room here for 100 iterations and don't go + // over 10% in percent_complete. + $task->set("queue", $queue); + $task->percent_complete = min($task->percent_complete + 0.1, 10); + $task->status = t2( + "Found one file", "Found %count files", + ORM::factory("videos_file")->where("task_id", "=", $task->id)->count_all()); + + if (!$queue) { + $task->set("mode", "add-files"); + $task->set( + "total_files", + ORM::factory("videos_file")->where("task_id", "=", $task->id)->count_all()); + $task->percent_complete = 10; + } + break; + + case "add-files": // 10% to 100% + $completed_files = $task->get("completed_files", 0); + $total_files = $task->get("total_files"); + + // Ordering by id ensures that we add them in the order that we created the entries, which + // will create albums first. Ignore entries which already have an Item_Model attached, + // they're done. + $entries = ORM::factory("videos_file") + ->where("task_id", "=", $task->id) + ->where("item_id", "IS", null) + ->order_by("id", "ASC") + ->limit(10) + ->find_all(); + if ($entries->count() == 0) { + // Out of entries, we're done. + $task->set("mode", "done"); + } + + $owner_id = identity::active_user()->id; + foreach ($entries as $entry) { + if (microtime(true) - $start > 0.5) { + break; + } + + // Look up the parent item for this entry. By now it should exist, but if none was + // specified, then this belongs as a child of the current item. + $parent_entry = ORM::factory("videos_file", $entry->parent_id); + if (!$parent_entry->loaded()) { + $parent = ORM::factory("item", $task->get("item_id")); + } else { + $parent = ORM::factory("item", $parent_entry->item_id); + } + + $name = basename($entry->file); + $title = item::convert_filename_to_title($name); + if (is_dir($entry->file)) { + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $parent->id; + $album->name = $name; + $album->title = $title; + $album->owner_id = $owner_id; + $album->save(); + $entry->item_id = $album->id; + } else { + try { + $extension = strtolower(pathinfo($name, PATHINFO_EXTENSION)); + if (in_array($extension, unserialize(module::get_var("videos", "allowed_extensions")))) { + $movie = ORM::factory("item"); + $movie->type = "movie"; + $movie->parent_id = $parent->id; + $movie->set_data_file($entry->file); + $movie->name = $name; + $movie->title = $title; + $movie->owner_id = $owner_id; + $movie->save(); + $entry->item_id = $movie->id; + $items_video = ORM::factory("items_video"); + $items_video->item_id = $movie->id; + $items_video->save(); + if (file_exists($entry->file . ".flv")) { + copy($entry->file . ".flv", $movie->resize_path() . ".flv"); + } + } else { + // This should never happen, because we don't add stuff to the list that we can't + // process. But just in, case.. set this to a non-null value so that we skip this + // entry. + $entry->item_id = 0; + $task->log("Skipping unknown file type: $entry->file"); + } + } catch (Exception $e) { + // This can happen if a photo file is invalid, like a BMP masquerading as a .jpg + $entry->item_id = 0; + $task->log("Skipping invalid file: $entry->file"); + } + } + + $completed_files++; + $entry->save(); + } + $task->set("completed_files", $completed_files); + $task->status = t("Adding photos / albums (%completed of %total)", + array("completed" => $completed_files, + "total" => $total_files)); + $task->percent_complete = $total_files ? 10 + 100 * ($completed_files / $total_files) : 100; + break; + + case "done": + batch::stop(); + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + db::build() + ->delete("videos_files") + ->where("task_id", "=", $task->id) + ->execute(); + message::info(t2("Successfully added one file", + "Successfully added %count files", + $task->get("completed_files"))); + } + } +} diff --git a/modules/videos/css/videos.css b/modules/videos/css/videos.css new file mode 100644 index 00000000..36746ab5 --- /dev/null +++ b/modules/videos/css/videos.css @@ -0,0 +1,38 @@ +#g-server-add button { + margin-bottom: .5em; +} + +#g-server-add-tree { + cursor: pointer; + padding-left: 4px; + width: 95%; +} + +#g-server-add-tree li { + padding: 0; + float: none; +} + +#g-server-add-tree span.selected { + background: #ddd; +} + +#g-server-add-tree { + border: 1px solid #ccc; + height: 20em; + overflow: auto; + margin-bottom: .5em; + padding: .5em; +} + +#g-server-add ul ul li { + padding-left: 1.2em; +} + +#g-server-add-paths li .ui-icon { + margin-top: .4em; +} + +#g-server-add-admin-form .textbox { + width: 400px; +} diff --git a/modules/videos/helpers/videos.php b/modules/videos/helpers/videos.php new file mode 100644 index 00000000..b75e4658 --- /dev/null +++ b/modules/videos/helpers/videos.php @@ -0,0 +1,49 @@ +Configure it now!", + array("url" => html::mark_clean(url::site("admin/videos")))), + "videos_configuration"); + } else { + site_status::clear("videos_configuration"); + } + } + + static function is_valid_path($path) { + if (!is_readable($path) || is_link($path)) { + return false; + } + + $authorized_paths = unserialize(module::get_var("videos", "authorized_paths")); + foreach (array_keys($authorized_paths) as $valid_path) { + if (strpos($path, $valid_path) === 0) { + return true; + } + } + + return false; + } +} diff --git a/modules/videos/helpers/videos_event.php b/modules/videos/helpers/videos_event.php new file mode 100644 index 00000000..54c7e860 --- /dev/null +++ b/modules/videos/helpers/videos_event.php @@ -0,0 +1,84 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("videos") + ->label(t("Videos")) + ->url(url::site("admin/videos"))); + } + + static function site_menu($menu, $theme) { + $item = $theme->item(); + $paths = unserialize(module::get_var("videos", "authorized_paths")); + + if ($item && identity::active_user()->admin && $item->is_album() && !empty($paths) && + is_writable($item->is_album() ? $item->file_path() : $item->parent()->file_path())) { + $menu->get("add_menu") + ->append(Menu::factory("dialog") + ->id("videos") + ->label(t("Add videos")) + ->url(url::site("videos/browse/$item->id"))); + } + } + + static function item_before_delete($item) { + // If deleting a video, make sure the resize is deleted as well, if it exists. + if ($item->is_movie()) { + $items_video = ORM::factory("items_video") + ->where("item_id", "=", $item->id) + ->find(); + if ($items_video->loaded() && file_exists($item->resize_path() . ".flv")) { + @unlink($item->resize_path() . ".flv"); + } + } + } + + static function item_updated($old, $new) { + // When updating a video, check and see if the file name is being changed. + // If so, check for and modify any corresponding resized video + + if ($old->is_movie()) { + if ($old->file_path() != $new->file_path()) { + $items_video = ORM::factory("items_video") + ->where("item_id", "=", $old->id) + ->find(); + if ($items_video->loaded() && file_exists($old->resize_path() . ".flv")) { + @rename($old->resize_path() . ".flv", $new->resize_path() . ".flv"); + } + } + } + } + + static function item_moved($item, $old_parent) { + // When moving an video, also move the flash resize, if it exists. + + if ($item->is_movie()) { + $items_video = ORM::factory("items_video") + ->where("item_id", "=", $item->id) + ->find(); + $old_resize_path = $old_parent->resize_path() . "/" . $item->name . ".flv"; + if ($items_video->loaded() && file_exists($old_resize_path)) { + @rename($old_resize_path, $item->resize_path() . ".flv"); + } + } + } +} diff --git a/modules/videos/helpers/videos_installer.php b/modules/videos/helpers/videos_installer.php new file mode 100644 index 00000000..137df9cb --- /dev/null +++ b/modules/videos/helpers/videos_installer.php @@ -0,0 +1,52 @@ +query("CREATE TABLE {videos_files} ( + `id` int(9) NOT NULL auto_increment, + `file` varchar(255) NOT NULL, + `item_id` int(9), + `parent_id` int(9), + `task_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + $db->query("CREATE TABLE {items_videos} ( + `id` int(9) NOT NULL auto_increment, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY (`item_id`, `id`)) + DEFAULT CHARSET=utf8;"); + module::set_var("videos", "allowed_extensions", serialize(array("avi", "mpg", "mpeg", "mov", "wmv", "asf", "mts"))); + module::set_version("videos", 1); + videos::check_config(); + } + + static function deactivate() { + site_status::clear("videos_configuration"); + } + + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {videos_files};"); + $db->query("DROP TABLE IF EXISTS {items_videos};"); + module::delete("videos"); + } +} diff --git a/modules/videos/helpers/videos_theme.php b/modules/videos/helpers/videos_theme.php new file mode 100644 index 00000000..9e994591 --- /dev/null +++ b/modules/videos/helpers/videos_theme.php @@ -0,0 +1,55 @@ +admin) { + $theme->css("videos.css"); + $theme->script("videos.js"); + } + + $item = $theme->item(); + if ($item && $item->is_movie()) { + $items_video = ORM::factory("items_video") + ->where("item_id", "=", $item->id) + ->find(); + if ($items_video->loaded()) { + $view = new View("videos_display_js.html"); + //$view->embed_code = addslashes($embedded_video->embed_code); + return $view; + } + } + } + + static function admin_head($theme) { + $head = array(); + if (strpos(Router::$current_uri, "admin/videos") !== false) { + $theme->css("videos.css"); + $theme->css("jquery.autocomplete.css"); + $base = url::site("__ARGS__"); + $csrf = access::csrf_token(); + $head[] = ""; + + $theme->script("jquery.autocomplete.js"); + $theme->script("admin_videos.js"); + } + + return implode("\n", $head); + } +} \ No newline at end of file diff --git a/modules/videos/js/admin_videos.js b/modules/videos/js/admin_videos.js new file mode 100644 index 00000000..9bb61ed1 --- /dev/null +++ b/modules/videos/js/admin_videos.js @@ -0,0 +1,8 @@ +/** + * Set up autocomplete on the server path list + * + */ +$("document").ready(function() { + $("#g-path").autocomplete( + base_url.replace("__ARGS__", "admin/videos/autocomplete"), {max: 256}); +}); diff --git a/modules/videos/js/videos.js b/modules/videos/js/videos.js new file mode 100644 index 00000000..02dda4c0 --- /dev/null +++ b/modules/videos/js/videos.js @@ -0,0 +1,125 @@ +(function($) { + $.widget("ui.gallery_server_add", { + _init: function() { + var self = this; + $("#g-server-add-add-button", this.element).click(function(event) { + event.preventDefault(); + $(".g-progress-bar", this.element). + progressbar(). + progressbar("value", 0); + $("#g-server-add-progress", this.element).slideDown("fast", function() { self.start_add(); }); + }); + $("#g-server-add-pause-button", this.element).click(function(event) { + self.pause = true; + $("#g-server-add-pause-button", this.element).hide(); + $("#g-server-add-continue-button", this.element).show(); + }); + $("#g-server-add-continue-button", this.element).click(function(event) { + self.pause = false; + $("#g-server-add-pause-button", this.element).show(); + $("#g-server-add-continue-button", this.element).hide(); + self.run_add(); + }); + $("#g-server-add-close-button", this.element).click(function(event) { + $("#g-dialog").dialog("close"); + window.location.reload(); + }); + $("#g-server-add-tree span.g-directory", this.element).dblclick(function(event) { + self.open_dir(event); + }); + $("#g-server-add-tree span.g-file, #g-server-add-tree span.g-directory", this.element).click(function(event) { + self.select_file(event); + }); + $("#g-server-add-tree span.g-directory", this.element).dblclick(function(event) { + self.open_dir(event); + }); + $("#g-dialog").bind("dialogclose", function(event, ui) { + window.location.reload(); + }); + }, + + taskURL: null, + pause: false, + + start_add: function() { + var self = this; + var paths = []; + $.each($("span.selected", self.element), function () { + paths.push($(this).attr("ref")); + }); + + $("#g-server-add-add-button", this.element).hide(); + $("#g-server-add-pause-button", this.element).show(); + + $.ajax({ + url: START_URL, + type: "POST", + async: false, + data: { "paths[]": paths }, + dataType: "json", + success: function(data, textStatus) { + $("#g-status").html(data.status); + $(".g-progress-bar", self.element).progressbar("value", data.percent_complete); + self.taskURL = data.url; + setTimeout(function() { self.run_add(); }, 25); + } + }); + return false; + }, + + run_add: function () { + var self = this; + $.ajax({ + url: self.taskURL, + async: false, + dataType: "json", + success: function(data, textStatus) { + $("#g-status").html(data.status); + $(".g-progress-bar", self.element).progressbar("value", data.percent_complete); + if (data.done) { + $("#g-server-add-progress", this.element).slideUp(); + $("#g-server-add-add-button", this.element).show(); + $("#g-server-add-pause-button", this.element).hide(); + $("#g-server-add-continue-button", this.element).hide(); + } else { + if (!self.pause) { + setTimeout(function() { self.run_add(); }, 25); + } + } + } + }); + }, + + /** + * Load a new directory + */ + open_dir: function(event) { + var self = this; + var path = $(event.target).attr("ref"); + $.ajax({ + url: GET_CHILDREN_URL.replace("__PATH__", path), + success: function(data, textStatus) { + $("#g-server-add-tree", self.element).html(data); + $("#g-server-add-tree span.g-directory", self.element).dblclick(function(event) { + self.open_dir(event); + }); + $("#g-server-add-tree span.g-file, #g-server-add-tree span.g-directory", this.element).click(function(event) { + self.select_file(event); + }); + } + }); + }, + + /** + * Manage file selection state. + */ + select_file: function (event) { + $(event.target).toggleClass("selected"); + if ($("#g-server-add span.selected").length) { + $("#g-server-add-add-button").enable(true).removeClass("ui-state-disabled"); + } else { + $("#g-server-add-add-button").enable(false).addClass("ui-state-disabled"); + } + } + }); +})(jQuery); diff --git a/modules/videos/models/items_video.php b/modules/videos/models/items_video.php new file mode 100644 index 00000000..67ee29e0 --- /dev/null +++ b/modules/videos/models/items_video.php @@ -0,0 +1,22 @@ + +
    +

    +
    + +

    + +
    +
    diff --git a/modules/videos/views/movieplayer.html.php b/modules/videos/views/movieplayer.html.php new file mode 100644 index 00000000..b960e1d7 --- /dev/null +++ b/modules/videos/views/movieplayer.html.php @@ -0,0 +1,33 @@ + +where("item_id", "=", $item->id) + ->find(); + if ($items_video->loaded() && file_exists($item->resize_path() . ".flv")) { + print html::anchor(str_replace("?m=", ".flv?m=", $item->resize_url(true)), "", $attrs); + } else { + print html::anchor($item->file_url(true), "", $attrs); + } +?> + + diff --git a/modules/videos/views/videos_display_js.html.php b/modules/videos/views/videos_display_js.html.php new file mode 100644 index 00000000..6273f27a --- /dev/null +++ b/modules/videos/views/videos_display_js.html.php @@ -0,0 +1,28 @@ + + +resize_path() . ".flv")) { ?> + + diff --git a/modules/videos/views/videos_tree.html.php b/modules/videos/views/videos_tree.html.php new file mode 100644 index 00000000..91354329 --- /dev/null +++ b/modules/videos/views/videos_tree.html.php @@ -0,0 +1,37 @@ + +
  • + + + + +
      + + +
    • + + + + +
        + + + +
      • + "> + " + ref="" > + + +
      • + + +
      • + + + +
      +
    • + + +
    +
  • diff --git a/modules/videos/views/videos_tree_dialog.html.php b/modules/videos/views/videos_tree_dialog.html.php new file mode 100644 index 00000000..a235ffbf --- /dev/null +++ b/modules/videos/views/videos_tree_dialog.html.php @@ -0,0 +1,52 @@ + + + +
    +

    html::purify($item->title))) ?>

    + +

    +
      + + parents() as $parent): ?> + > title) ?> + + +
    • title) ?>
    • +
    + +
      + +
    + + + + + + + + + + + + + +
    From f1b2952618ece99aa9552eae167f6c26c20a0c06 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Wed, 29 Sep 2010 15:36:37 -0400 Subject: [PATCH 015/300] Added support for .flv formatted resized videos. --- modules/videos/controllers/videos.php | 4 ++++ modules/videos/views/admin_videos.html.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/videos/controllers/videos.php b/modules/videos/controllers/videos.php index 00ec0691..020817ff 100644 --- a/modules/videos/controllers/videos.php +++ b/modules/videos/controllers/videos.php @@ -256,6 +256,10 @@ class Videos_Controller extends Admin_Controller { $items_video->save(); if (file_exists($entry->file . ".flv")) { copy($entry->file . ".flv", $movie->resize_path() . ".flv"); + list ($vid_width, $vid_height, $mime_type) = movie::get_file_metadata($entry->file . ".flv"); + $movie->height = $vid_height; + $movie->width = $vid_width; + $movie->save(); } } else { // This should never happen, because we don't add stuff to the list that we can't diff --git a/modules/videos/views/admin_videos.html.php b/modules/videos/views/admin_videos.html.php index 2787bbbb..6addb355 100644 --- a/modules/videos/views/admin_videos.html.php +++ b/modules/videos/views/admin_videos.html.php @@ -1,6 +1,6 @@
    -

    +

    From 6af5b00a3b42dd25a812bb6d6212dc7278dcc74c Mon Sep 17 00:00:00 2001 From: rWatcher Date: Thu, 7 Oct 2010 13:54:46 -0400 Subject: [PATCH 016/300] Updated for Gallery 3.0 Release. --- modules/phpmailer/libraries/Sendmail.php | 94 ++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/modules/phpmailer/libraries/Sendmail.php b/modules/phpmailer/libraries/Sendmail.php index 30288969..c24621a2 100644 --- a/modules/phpmailer/libraries/Sendmail.php +++ b/modules/phpmailer/libraries/Sendmail.php @@ -91,6 +91,100 @@ class Sendmail_Core { return $this; } +headers = array(); + $this->from(module::get_var("gallery", "email_from", "")); + $this->reply_to(module::get_var("gallery", "email_reply_to", "")); + $this->line_length(module::get_var("gallery", "email_line_length", 70)); + $separator = module::get_var("gallery", "email_header_separator", null); + $this->header_separator(empty($separator) ? "\n" : unserialize($separator)); + } + + public function __get($key) { + return null; + } + + public function __call($key, $value) { + switch ($key) { + case "to": + $this->to = is_array($value[0]) ? $value[0] : array($value[0]); + break; + case "header": + if (count($value) != 2) { + Kohana_Log::add("error", wordwrap("Invalid header parameters\n" . Kohana::debug($value))); + throw new Exception("@todo INVALID_HEADER_PARAMETERS"); + } + $this->headers[$value[0]] = $value[1]; + break; + case "from": + $this->headers["From"] = $value[0]; + break; + case "reply_to": + $this->headers["Reply-To"] = $value[0]; + break; + default: + $this->$key = $value[0]; + } + return $this; + } + + public function send() { + if (empty($this->to)) { + Kohana_Log::add("error", wordwrap("Sending mail failed:\nNo to address specified")); + throw new Exception("@todo TO_IS_REQUIRED_FOR_MAIL"); + } + $to = implode(", ", $this->to); + $headers = array(); + foreach ($this->headers as $key => $value) { + $key = ucfirst($key); + $headers[] = "$key: $value"; + } + + // The docs say headers should be separated by \r\n, but occasionaly that doesn't work and you + // need to use a single \n. This can be set in config/sendmail.php + $headers = implode($this->header_separator, $headers); + $message = wordwrap($this->message, $this->line_length, "\n"); + if (!$this->mail($to, $this->subject, $message, $headers)) { + throw new Exception("@todo SEND_MAIL_FAILED"); + } + return $this; + } + public function mail($to, $subject, $message, $headers) { // This function is completely different from the original // Gallery Sendmail script. Outside of this function, From 0eb67f2fadbddecc7769fd6b88a96e55a4a6c4c4 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Thu, 7 Oct 2010 14:14:46 -0400 Subject: [PATCH 017/300] Updated for Gallery 3.0 Release, this time with working code... --- modules/phpmailer/libraries/Sendmail.php | 95 +----------------------- 1 file changed, 1 insertion(+), 94 deletions(-) diff --git a/modules/phpmailer/libraries/Sendmail.php b/modules/phpmailer/libraries/Sendmail.php index c24621a2..b53722c1 100644 --- a/modules/phpmailer/libraries/Sendmail.php +++ b/modules/phpmailer/libraries/Sendmail.php @@ -1,96 +1,3 @@ -headers = array(); - $config = Kohana::config("sendmail"); - foreach ($config as $key => $value) { - $this->$key($value); - } - } - - public function __get($key) { - return null; - } - - public function __call($key, $value) { - switch ($key) { - case "to": - $this->to = is_array($value[0]) ? $value[0] : array($value[0]); - break; - case "header": - if (count($value) != 2) { - Kohana_Log::add("error", wordwrap("Invalid header parameters\n" . Kohana::debug($value))); - throw new Exception("@todo INVALID_HEADER_PARAMETERS"); - } - $this->headers[$value[0]] = $value[1]; - break; - case "from": - $this->headers["From"] = $value[0]; - break; - case "reply_to": - $this->headers["Reply-To"] = $value[0]; - break; - default: - $this->$key = $value[0]; - } - return $this; - } - - public function send() { - if (empty($this->to)) { - Kohana_Log::add("error", wordwrap("Sending mail failed:\nNo to address specified")); - throw new Exception("@todo TO_IS_REQUIRED_FOR_MAIL"); - } - $to = implode(", ", $this->to); - $headers = array(); - foreach ($this->headers as $key => $value) { - $key = ucfirst($key); - $headers[] = "$key: $value"; - } - - // The docs say headers should be separated by \r\n, but occasionaly that doesn't work and you - // need to use a single \n. This can be set in config/sendmail.php - $headers = implode($this->header_separator, $headers); - $message = wordwrap($this->message, $this->line_length, "\n"); - if (!$this->mail($to, $this->subject, $message, $headers)) { - throw new Exception("@todo SEND_MAIL_FAILED"); - } - return $this; - } - Send(); } -} +} \ No newline at end of file From 2a12348c53b69e8f27fec6207ec12f7baacf4e0d Mon Sep 17 00:00:00 2001 From: rWatcher Date: Thu, 7 Oct 2010 15:54:56 -0400 Subject: [PATCH 018/300] Added captcha support. --- .../contactowner/controllers/contactowner.php | 136 ++++++++++-------- 1 file changed, 73 insertions(+), 63 deletions(-) diff --git a/modules/contactowner/controllers/contactowner.php b/modules/contactowner/controllers/contactowner.php index 2a760762..86a6fe9c 100644 --- a/modules/contactowner/controllers/contactowner.php +++ b/modules/contactowner/controllers/contactowner.php @@ -18,6 +18,55 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class ContactOwner_Controller extends Controller { + static function get_email_form($user_id) { + // Determine name of the person the message is going to. + $str_to_name = ""; + if ($user_id == -1) { + $str_to_name = module::get_var("contactowner", "contact_owner_name"); + } else { + // Locate the record for the user specified by $user_id, + // use this to determine the user's name. + $userDetails = ORM::factory("user") + ->where("id", "=", $user_id) + ->find_all(); + $str_to_name = $userDetails[0]->name; + } + + // Make a new form with a couple of text boxes. + $form = new Forge("contactowner/sendemail/{$user_id}", "", "post", + array("id" => "g-contact-owner-send-form")); + $sendmail_fields = $form->group("contactOwner"); + $sendmail_fields->input("email_to") + ->label(t("To:"))->value($str_to_name) + ->id("g-contactowner-to-name"); + $sendmail_fields->input("email_from") + ->label(t("From:"))->value(identity::active_user()->email) + ->id("g-contactowner-from-email") + ->rules('required|valid_email') + ->error_messages("required", t("You must enter a valid email address")) + ->error_messages("valid_email", t("You must enter a valid email address")) + ->error_messages("invalid", t("You must enter a valid email address")); + $sendmail_fields->input("email_subject") + ->label(t("Subject:"))->value("") + ->id("g-contactowner-subject") + ->rules('required') + ->error_messages("required", t("You must enter a subject")); + $sendmail_fields->textarea("email_body") + ->label(t("Message:")) + ->value("") + ->id("g-contactowner-email-body") + ->rules('required') + ->error_messages("required", t("You must enter a message")); + + // Add a captcha, if there's an active captcha module. + module::event("captcha_protect_form", $form); + + // Add a save button to the form. + $sendmail_fields->submit("SendMessage")->value(t("Send")); + + return $form; + } + public function emailowner() { // Display a form that a vistor can use to contact the site owner. @@ -26,24 +75,10 @@ class ContactOwner_Controller extends Controller { throw new Kohana_404_Exception(); } - // Make a new form with a couple of text boxes. - $form = new Forge("contactowner/sendemail", "", "post", - array("id" => "g-contact-owner-send-form")); - $sendmail_fields = $form->group("contactOwner"); - $sendmail_fields->input("email_to")->label(t("To:"))->value(module::get_var("contactowner", "contact_owner_name")); - $sendmail_fields->input("email_from")->label(t("From:"))->value(identity::active_user()->email); - - $sendmail_fields->input("email_subject")->label(t("Subject:"))->value(""); - $sendmail_fields->textarea("email_body")->label(t("Message:"))->value(""); - $sendmail_fields->hidden("email_to_id")->value("-1"); - - // Add a save button to the form. - $sendmail_fields->submit("SendMessage")->value(t("Send")); - // Set up and display the actual page. $template = new Theme_View("page.html", "other", "Contact"); $template->content = new View("contactowner_emailform.html"); - $template->content->sendmail_form = $form; + $template->content->sendmail_form = $this->get_email_form("-1"); print $template; } @@ -55,49 +90,29 @@ class ContactOwner_Controller extends Controller { throw new Kohana_404_Exception(); } - // Locate the record for the user specified by $user_id, - // use this to determine the user's name. - $userDetails = ORM::factory("user") - ->where("id", "=", $user_id) - ->find_all(); - - // Make a new form with a couple of text boxes. - $form = new Forge("contactowner/sendemail", "", "post", - array("id" => "g-contact-owner-send-form")); - $sendmail_fields = $form->group("contactOwner"); - $sendmail_fields->input("email_to")->label(t("To:"))->value($userDetails[0]->name); - $sendmail_fields->input("email_from")->label(t("From:"))->value(identity::active_user()->email); - $sendmail_fields->input("email_subject")->label(t("Subject:"))->value(""); - $sendmail_fields->textarea("email_body")->label(t("Message:"))->value(""); - $sendmail_fields->hidden("email_to_id")->value($user_id); - - // Add a save button to the form. - $sendmail_fields->submit("SendMessage")->value(t("Send")); - // Set up and display the actual page. $template = new Theme_View("page.html", "other", "Contact"); $template->content = new View("contactowner_emailform.html"); - $template->content->sendmail_form = $form; + $template->content->sendmail_form = $this->get_email_form($user_id); print $template; } - public function sendemail() { - // Process the data from the form into an email, - // then send the email. + public function sendemail($user_id) { + // Validate the form, then send the actual email. - // Make sure the form was submitted. - if ($_POST) { - // Set up some rules to validate the form against. - $post = new Validation($_POST); - $post->add_rules('email_from', 'required', 'valid::email'); - $post->add_rules('email_subject', 'required'); - $post->add_rules('email_body', 'required'); - - // If the form was filled out properly then... - if ($post->validate()) { + // If this page is disabled, show a 404 error. + if (($user_id == "-1") && (module::get_var("contactowner", "contact_owner_link") != true)) { + throw new Kohana_404_Exception(); + } elseif (($user_id >= 0) && (module::get_var("contactowner", "contact_user_link") != true)) { + throw new Kohana_404_Exception(); + } + + // Make sure the form submission was valid. + $form = $this->get_email_form($user_id); + $valid = $form->validate(); + if ($valid) { // Copy the data from the email form into a couple of variables. $str_emailsubject = Input::instance()->post("email_subject"); - $str_emailtoid = Input::instance()->post("email_to_id"); $str_emailfrom = Input::instance()->post("email_from"); $str_emailbody = Input::instance()->post("email_body"); @@ -111,14 +126,14 @@ class ContactOwner_Controller extends Controller { // Figure out where the email is going to. $str_emailto = ""; - if ($str_emailtoid == -1) { + if ($user_id == -1) { // If the email id is "-1" send the message to a pre-determined // owner email address. $str_emailto = module::get_var("contactowner", "contact_owner_email"); } else { // or else grab the email from the user table. $userDetails = ORM::factory("user") - ->where("id", "=", $str_emailtoid) + ->where("id", "=", $user_id) ->find_all(); $str_emailto = $userDetails[0]->email; } @@ -137,18 +152,13 @@ class ContactOwner_Controller extends Controller { $template->content = new View("contactowner_emailform.html"); $template->content->sendmail_form = t("Your Message Has Been Sent."); print $template; - } else { - // Display a message telling the visitor that their email has been not been sent, - // along with the reason(s) why. - $template = new Theme_View("page.html", "other", "Contact"); - $template->content = new View("contactowner_emailform.html"); - $template->content->sendmail_form = t("Your Message Has Not Been Sent."); - $template->content->sendmail_form = $template->content->sendmail_form . "

    " . t("Reason(s):") . "
    "; - foreach($post->errors('form_error_messages') as $error) { - $template->content->sendmail_form = $template->content->sendmail_form . " - " . t($error) . "
    "; - } + + } else { + // Set up and display the actual page. + $template = new Theme_View("page.html", "other", "Contact"); + $template->content = new View("contactowner_emailform.html"); + $template->content->sendmail_form = $form; print $template; - } } } -} \ No newline at end of file +} From 18aff8bfad727db9b8c0c78cf1dcc6a142662c3e Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Wed, 27 Oct 2010 22:25:36 +0200 Subject: [PATCH 019/300] Fix: when downloading entire gallery, the filename were ".zip" --- 3.0/modules/downloadalbum/controllers/downloadalbum.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/3.0/modules/downloadalbum/controllers/downloadalbum.php b/3.0/modules/downloadalbum/controllers/downloadalbum.php index c2f939f7..201887cf 100644 --- a/3.0/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.0/modules/downloadalbum/controllers/downloadalbum.php @@ -26,6 +26,11 @@ class downloadalbum_Controller extends Controller { $album = $this->init($id); $files = $this->getFilesList($album); + // ZIP name + $zipname = (empty($album->name)) + ? $zipname = 'Gallery.zip' // @todo $zipname = purified_version_of($album->title) + : $album->name.'.zip'; + // Calculate ZIP size (look behind for details) $zipsize = 22; foreach($files as $f) { @@ -34,7 +39,7 @@ class downloadalbum_Controller extends Controller { // Send headers $this->prepareOutput(); - $this->sendHeaders($album->name.'.zip', $zipsize); + $this->sendHeaders($zipname, $zipsize); // Generate and send ZIP file // http://www.pkware.com/documents/casestudies/APPNOTE.TXT (v6.3.2) From 843632c30ae8f078e8f105943241e59f79712858 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Wed, 27 Oct 2010 23:21:03 +0200 Subject: [PATCH 020/300] Use exceptions: it is better to display en error screen than an empty screen --- .../downloadalbum/controllers/downloadalbum.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/3.0/modules/downloadalbum/controllers/downloadalbum.php b/3.0/modules/downloadalbum/controllers/downloadalbum.php index 201887cf..c3696263 100644 --- a/3.0/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.0/modules/downloadalbum/controllers/downloadalbum.php @@ -141,9 +141,7 @@ class downloadalbum_Controller extends Controller { // Only send an album if (!$item->is_album()) { - // @todo: throw an exception? - Kohana::log('error', 'item is not an album: '.$item->relative_path()); - exit; + throw new Kohana_Exception('item is not an album: '.$item->relative_path()); } // Must have view_full to download the originals files @@ -161,9 +159,7 @@ class downloadalbum_Controller extends Controller { // Go to the parent of album so the ZIP will not contains all the // server hierarchy if (!chdir($album->file_path().'/../')) { - // @todo: throw an exception? - Kohana::log('error', 'unable to chdir('.$item->file_path().'/../)'); - exit; + throw new Kohana_Exception('unable to chdir('.$item->file_path().'/../)'); } $cwd = getcwd(); @@ -183,9 +179,7 @@ class downloadalbum_Controller extends Controller { } if (count($files) === 0) { - // @todo: throw an exception? - Kohana::log('error', 'no zippable files in ['.$album->relative_path().']'); - exit; + throw new Kohana_Exception('no zippable files in ['.$album->relative_path().']'); } return $files; From 13eeb2bb2395e6fc01ad6bdc7f8895a5f2bde107 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Wed, 27 Oct 2010 23:45:22 +0200 Subject: [PATCH 021/300] Prepare the future: server-path can be different of zip-path (tag, resize/full, ...) --- .../controllers/downloadalbum.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/3.0/modules/downloadalbum/controllers/downloadalbum.php b/3.0/modules/downloadalbum/controllers/downloadalbum.php index c3696263..62eb90f7 100644 --- a/3.0/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.0/modules/downloadalbum/controllers/downloadalbum.php @@ -33,8 +33,8 @@ class downloadalbum_Controller extends Controller { // Calculate ZIP size (look behind for details) $zipsize = 22; - foreach($files as $f) { - $zipsize += 76 + 2*strlen($f) + filesize($f); + foreach($files as $f_name => $f_path) { + $zipsize += 76 + 2*strlen($f_name) + filesize($f_path); } // Send headers @@ -46,11 +46,11 @@ class downloadalbum_Controller extends Controller { $lfh_offset = 0; $cds = ''; $cds_offset = 0; - foreach($files as $f) { - $f_namelen = strlen($f); - $f_size = filesize($f); - $f_mtime = $this->unix2dostime(filemtime($f)); - $f_crc32 = $this->fixBug45028(hexdec(hash_file('crc32b', $f, false))); + foreach($files as $f_name => $f_path) { + $f_namelen = strlen($f_name); + $f_size = filesize($f_path); + $f_mtime = $this->unix2dostime(filemtime($f_path)); + $f_crc32 = $this->fixBug45028(hexdec(hash_file('crc32b', $f_path, false))); // Local file header echo pack('VvvvVVVVvva' . $f_namelen, @@ -65,12 +65,12 @@ class downloadalbum_Controller extends Controller { $f_namelen, // file name length (2 bytes) 0, // extra field length (2 bytes) - $f // file name (variable size) + $f_name // file name (variable size) // extra field (variable size) => n/a ); // File data - readfile($f); + readfile($f_path); // Data descriptor (n/a) @@ -93,7 +93,7 @@ class downloadalbum_Controller extends Controller { 0x81b40000, // external file attributes (4 bytes) => chmod 664 $lfh_offset, // relative offset of local header (4 bytes) - $f // file name (variable size) + $f_name // file name (variable size) // extra field (variable size) => n/a // file comment (variable size) => n/a ); @@ -175,7 +175,7 @@ class downloadalbum_Controller extends Controller { continue; } - $files[] = $relative_path; + $files[$relative_path] = $relative_path; } if (count($files) === 0) { From c37dca092296a0ea97a88932886690be05a4c29a Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Thu, 28 Oct 2010 00:50:21 +0200 Subject: [PATCH 022/300] Add support for downloading by tag --- .../controllers/downloadalbum.php | 108 +++++++++++------- .../helpers/downloadalbum_event.php | 26 +++-- .../helpers/downloadalbum_theme.php | 4 +- 3.0/modules/downloadalbum/module.info | 2 +- 4 files changed, 85 insertions(+), 55 deletions(-) diff --git a/3.0/modules/downloadalbum/controllers/downloadalbum.php b/3.0/modules/downloadalbum/controllers/downloadalbum.php index 62eb90f7..76dd7d8d 100644 --- a/3.0/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.0/modules/downloadalbum/controllers/downloadalbum.php @@ -22,14 +22,34 @@ class downloadalbum_Controller extends Controller { /** * Generate a ZIP on-the-fly. */ - public function zip($id) { - $album = $this->init($id); - $files = $this->getFilesList($album); + public function zip($container_type, $id) { + switch($container_type) { + case "album": + $container = ORM::factory("item", $id); + if (!$container->is_album()) { + throw new Kohana_Exception('container is not an album: '.$container->relative_path()); + } - // ZIP name - $zipname = (empty($album->name)) - ? $zipname = 'Gallery.zip' // @todo $zipname = purified_version_of($album->title) - : $album->name.'.zip'; + $zipname = (empty($container->name)) + ? 'Gallery.zip' // @todo purified_version_of($container->title).'.zip' + : $container->name.'.zip'; + break; + + case "tag": + // @todo: if the module is not installed, it crash + $container = ORM::factory("tag", $id); + if (is_null($container->name)) { + throw new Kohana_Exception('container is not a tag: '.$id); + } + + $zipname = $container->name.'.zip'; + break; + + default: + throw new Kohana_Exception('unhandled container type: '.$container_type); + } + + $files = $this->getFilesList($container); // Calculate ZIP size (look behind for details) $zipsize = 22; @@ -133,53 +153,57 @@ class downloadalbum_Controller extends Controller { } - /** - * Init - */ - private function init($id) { - $item = ORM::factory("item", $id); - - // Only send an album - if (!$item->is_album()) { - throw new Kohana_Exception('item is not an album: '.$item->relative_path()); - } - - // Must have view_full to download the originals files - access::required("view_full", $item); - - return $item; - } - /** * Return the files that must be included in the archive. */ - private function getFilesList($album) { + private function getFilesList($container) { $files = array(); - // Go to the parent of album so the ZIP will not contains all the - // server hierarchy - if (!chdir($album->file_path().'/../')) { - throw new Kohana_Exception('unable to chdir('.$item->file_path().'/../)'); - } - $cwd = getcwd(); + if( $container instanceof Item_Model && $container->is_album() ) { + $container_realpath = realpath($container->file_path().'/../'); - $items = $album->viewable() - ->descendants(null, null, array(array("type", "<>", "album"))); - foreach($items as $i) { - if (!access::can('view_full', $i)) { - continue; + $items = $container->viewable() + ->descendants(null, null, array(array("type", "<>", "album"))); + foreach($items as $i) { + if (!access::can('view_full', $i)) { + continue; + } + + $i_realpath = realpath($i->file_path()); + if (!is_readable($i_realpath)) { + continue; + } + + $i_relative_path = str_replace($container_realpath.'/', '', $i_realpath); + $files[$i_relative_path] = $i_realpath; } - $relative_path = str_replace($cwd.'/', '', realpath($i->file_path())); - if (!is_readable($relative_path)) { - continue; - } + } else if( $container instanceof Tag_Model ) { + $items = $container->items(); + foreach($items as $i) { + if (!access::can('view_full', $i)) { + continue; + } - $files[$relative_path] = $relative_path; + if( $i->is_album() ) { + foreach($this->getFilesList($i) as $f_name => $f_path) { + $files[$container->name.'/'.$f_name] = $f_path; + } + + } else { + $i_realpath = realpath($i->file_path()); + if (!is_readable($i_realpath)) { + continue; + } + + $i_relative_path = $container->name.'/'.$i->name; + $files[$i_relative_path] = $i_realpath; + } + } } if (count($files) === 0) { - throw new Kohana_Exception('no zippable files in ['.$album->relative_path().']'); + throw new Kohana_Exception('no zippable files in ['.$container->name.']'); } return $files; diff --git a/3.0/modules/downloadalbum/helpers/downloadalbum_event.php b/3.0/modules/downloadalbum/helpers/downloadalbum_event.php index e2fe4420..e58512ac 100644 --- a/3.0/modules/downloadalbum/helpers/downloadalbum_event.php +++ b/3.0/modules/downloadalbum/helpers/downloadalbum_event.php @@ -19,14 +19,22 @@ */ class downloadalbum_event_Core { static function album_menu($menu, $theme) { - if (access::can("view_full", $theme->item)) { - $downloadLink = url::site("downloadalbum/zip/{$theme->item->id}"); - $menu - ->append(Menu::factory("link") - ->id("downloadalbum") - ->label(t("Download Album")) - ->url($downloadLink) - ->css_id("g-download-album-link")); - } + $downloadLink = url::site("downloadalbum/zip/album/{$theme->item->id}"); + $menu + ->append(Menu::factory("link") + ->id("downloadalbum") + ->label(t("Download Album")) + ->url($downloadLink) + ->css_id("g-download-album-link")); } + + static function tag_menu($menu, $theme) { + $downloadLink = url::site("downloadalbum/zip/tag/{$theme->tag()->id}"); + $menu + ->append(Menu::factory("link") + ->id("downloadalbum") + ->label(t("Download Album")) + ->url($downloadLink) + ->css_id("g-download-album-link")); + } } diff --git a/3.0/modules/downloadalbum/helpers/downloadalbum_theme.php b/3.0/modules/downloadalbum/helpers/downloadalbum_theme.php index 2fd23552..d88d8a52 100644 --- a/3.0/modules/downloadalbum/helpers/downloadalbum_theme.php +++ b/3.0/modules/downloadalbum/helpers/downloadalbum_theme.php @@ -19,8 +19,6 @@ */ class downloadalbum_theme { static function head($theme) { - if ($theme->item && access::can("view_full", $theme->item)) { - $theme->css("downloadalbum_menu.css"); - } + $theme->css("downloadalbum_menu.css"); } } diff --git a/3.0/modules/downloadalbum/module.info b/3.0/modules/downloadalbum/module.info index 177ad4a8..11c52ba2 100644 --- a/3.0/modules/downloadalbum/module.info +++ b/3.0/modules/downloadalbum/module.info @@ -1,3 +1,3 @@ name = "DownloadAlbum" description = "Displays a link to download a ZIP archive of the current album." -version = 1 +version = 2 From 1160b93e87b629938743341ef0bfea5c3f941673 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Fri, 29 Oct 2010 00:39:44 -0400 Subject: [PATCH 023/300] Added REST support. --- .../itemchecksum/controllers/itemchecksum.php | 2 +- .../helpers/item_itemchecksums_rest.php | 42 ++++++++++++++ .../helpers/itemchecksum_md5_rest.php | 55 +++++++++++++++++++ .../helpers/itemchecksum_rest.php | 41 ++++++++++++++ .../helpers/itemchecksum_sha1_rest.php | 55 +++++++++++++++++++ .../itemchecksum/controllers/itemchecksum.php | 2 +- .../helpers/item_itemchecksums_rest.php | 42 ++++++++++++++ .../helpers/itemchecksum_md5_rest.php | 55 +++++++++++++++++++ .../helpers/itemchecksum_rest.php | 41 ++++++++++++++ .../helpers/itemchecksum_sha1_rest.php | 55 +++++++++++++++++++ 10 files changed, 388 insertions(+), 2 deletions(-) create mode 100644 3.0/modules/itemchecksum/helpers/item_itemchecksums_rest.php create mode 100644 3.0/modules/itemchecksum/helpers/itemchecksum_md5_rest.php create mode 100644 3.0/modules/itemchecksum/helpers/itemchecksum_rest.php create mode 100644 3.0/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php create mode 100644 3.1/modules/itemchecksum/helpers/item_itemchecksums_rest.php create mode 100644 3.1/modules/itemchecksum/helpers/itemchecksum_md5_rest.php create mode 100644 3.1/modules/itemchecksum/helpers/itemchecksum_rest.php create mode 100644 3.1/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php diff --git a/3.0/modules/itemchecksum/controllers/itemchecksum.php b/3.0/modules/itemchecksum/controllers/itemchecksum.php index 01cbfc4f..7123350d 100644 --- a/3.0/modules/itemchecksum/controllers/itemchecksum.php +++ b/3.0/modules/itemchecksum/controllers/itemchecksum.php @@ -86,4 +86,4 @@ class itemchecksum_Controller extends Controller { print "0"; } } -} \ No newline at end of file +} diff --git a/3.0/modules/itemchecksum/helpers/item_itemchecksums_rest.php b/3.0/modules/itemchecksum/helpers/item_itemchecksums_rest.php new file mode 100644 index 00000000..3f109f66 --- /dev/null +++ b/3.0/modules/itemchecksum/helpers/item_itemchecksums_rest.php @@ -0,0 +1,42 @@ +url); + access::required("view", $item); + + $checksums = array(rest::url("itemchecksum_md5", $item), rest::url("itemchecksum_sha1", $item)); + return array( + "url" => $request->url, + "members" => $checksums); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/item_checksums/{$item->id}"); + } +} diff --git a/3.0/modules/itemchecksum/helpers/itemchecksum_md5_rest.php b/3.0/modules/itemchecksum/helpers/itemchecksum_md5_rest.php new file mode 100644 index 00000000..4cd6dfa9 --- /dev/null +++ b/3.0/modules/itemchecksum/helpers/itemchecksum_md5_rest.php @@ -0,0 +1,55 @@ +url); + access::required("view", $item); + $checksum = "0"; + // If the KeepOriginal module is active, check for/use the + // original image instead of the gallery edited version. + if (module::is_active("keeporiginal")) { + $original_image = VARPATH . "original/" . str_replace(VARPATH . "albums/", "", $item->file_path()); + if ($item->is_photo() && file_exists($original_image)) { + $checksum = md5_file($original_image); + } else { + $checksum = md5_file($item->file_path()); + } + } else { + $checksum = md5_file($item->file_path()); + } + $data = array("checksum" => $checksum); + + return array( + "url" => $request->url, + "entity" => $data); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/itemchecksum_md5/{$item->id}"); + } +} diff --git a/3.0/modules/itemchecksum/helpers/itemchecksum_rest.php b/3.0/modules/itemchecksum/helpers/itemchecksum_rest.php new file mode 100644 index 00000000..a1404eaa --- /dev/null +++ b/3.0/modules/itemchecksum/helpers/itemchecksum_rest.php @@ -0,0 +1,41 @@ + array( + "url" => rest::url("item_itemchecksums", $resource))); + } + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/itemchecksum/{$item->id}"); + } +} diff --git a/3.0/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php b/3.0/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php new file mode 100644 index 00000000..9bb5f118 --- /dev/null +++ b/3.0/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php @@ -0,0 +1,55 @@ +url); + access::required("view", $item); + $checksum = "0"; + // If the KeepOriginal module is active, check for/use the + // original image instead of the gallery edited version. + if (module::is_active("keeporiginal")) { + $original_image = VARPATH . "original/" . str_replace(VARPATH . "albums/", "", $item->file_path()); + if ($item->is_photo() && file_exists($original_image)) { + $checksum = sha1_file($original_image); + } else { + $checksum = sha1_file($item->file_path()); + } + } else { + $checksum = sha1_file($item->file_path()); + } + $data = array("checksum" => $checksum); + + return array( + "url" => $request->url, + "entity" => $data); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/itemchecksum_sha1/{$item->id}"); + } +} diff --git a/3.1/modules/itemchecksum/controllers/itemchecksum.php b/3.1/modules/itemchecksum/controllers/itemchecksum.php index 01cbfc4f..7123350d 100644 --- a/3.1/modules/itemchecksum/controllers/itemchecksum.php +++ b/3.1/modules/itemchecksum/controllers/itemchecksum.php @@ -86,4 +86,4 @@ class itemchecksum_Controller extends Controller { print "0"; } } -} \ No newline at end of file +} diff --git a/3.1/modules/itemchecksum/helpers/item_itemchecksums_rest.php b/3.1/modules/itemchecksum/helpers/item_itemchecksums_rest.php new file mode 100644 index 00000000..3f109f66 --- /dev/null +++ b/3.1/modules/itemchecksum/helpers/item_itemchecksums_rest.php @@ -0,0 +1,42 @@ +url); + access::required("view", $item); + + $checksums = array(rest::url("itemchecksum_md5", $item), rest::url("itemchecksum_sha1", $item)); + return array( + "url" => $request->url, + "members" => $checksums); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/item_checksums/{$item->id}"); + } +} diff --git a/3.1/modules/itemchecksum/helpers/itemchecksum_md5_rest.php b/3.1/modules/itemchecksum/helpers/itemchecksum_md5_rest.php new file mode 100644 index 00000000..4cd6dfa9 --- /dev/null +++ b/3.1/modules/itemchecksum/helpers/itemchecksum_md5_rest.php @@ -0,0 +1,55 @@ +url); + access::required("view", $item); + $checksum = "0"; + // If the KeepOriginal module is active, check for/use the + // original image instead of the gallery edited version. + if (module::is_active("keeporiginal")) { + $original_image = VARPATH . "original/" . str_replace(VARPATH . "albums/", "", $item->file_path()); + if ($item->is_photo() && file_exists($original_image)) { + $checksum = md5_file($original_image); + } else { + $checksum = md5_file($item->file_path()); + } + } else { + $checksum = md5_file($item->file_path()); + } + $data = array("checksum" => $checksum); + + return array( + "url" => $request->url, + "entity" => $data); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/itemchecksum_md5/{$item->id}"); + } +} diff --git a/3.1/modules/itemchecksum/helpers/itemchecksum_rest.php b/3.1/modules/itemchecksum/helpers/itemchecksum_rest.php new file mode 100644 index 00000000..a1404eaa --- /dev/null +++ b/3.1/modules/itemchecksum/helpers/itemchecksum_rest.php @@ -0,0 +1,41 @@ + array( + "url" => rest::url("item_itemchecksums", $resource))); + } + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/itemchecksum/{$item->id}"); + } +} diff --git a/3.1/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php b/3.1/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php new file mode 100644 index 00000000..9bb5f118 --- /dev/null +++ b/3.1/modules/itemchecksum/helpers/itemchecksum_sha1_rest.php @@ -0,0 +1,55 @@ +url); + access::required("view", $item); + $checksum = "0"; + // If the KeepOriginal module is active, check for/use the + // original image instead of the gallery edited version. + if (module::is_active("keeporiginal")) { + $original_image = VARPATH . "original/" . str_replace(VARPATH . "albums/", "", $item->file_path()); + if ($item->is_photo() && file_exists($original_image)) { + $checksum = sha1_file($original_image); + } else { + $checksum = sha1_file($item->file_path()); + } + } else { + $checksum = sha1_file($item->file_path()); + } + $data = array("checksum" => $checksum); + + return array( + "url" => $request->url, + "entity" => $data); + } + + static function resolve($id) { + $item = ORM::factory("item", $id); + if (!access::can("view", $item)) { + throw new Kohana_404_Exception(); + } + return $item; + } + + static function url($item) { + return url::abs_site("rest/itemchecksum_sha1/{$item->id}"); + } +} From d2d0d3e52a852c61b6d6ec223652b881a93012a6 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Fri, 29 Oct 2010 01:51:15 -0400 Subject: [PATCH 024/300] Initial commit of albumpassword module. --- .../controllers/albumpassword.php | 152 ++++++++++++++++++ 3.0/modules/albumpassword/helpers/MY_item.php | 53 ++++++ .../helpers/albumpassword_event.php | 104 ++++++++++++ .../helpers/albumpassword_installer.php | 42 +++++ .../models/items_albumpassword.php | 21 +++ 3.0/modules/albumpassword/module.info | 3 + .../views/assignpassword.html.php | 24 +++ .../views/loginpassword.html.php | 24 +++ .../controllers/albumpassword.php | 152 ++++++++++++++++++ 3.1/modules/albumpassword/helpers/MY_item.php | 53 ++++++ .../helpers/albumpassword_event.php | 104 ++++++++++++ .../helpers/albumpassword_installer.php | 42 +++++ .../models/items_albumpassword.php | 21 +++ 3.1/modules/albumpassword/module.info | 3 + .../views/assignpassword.html.php | 24 +++ .../views/loginpassword.html.php | 24 +++ 16 files changed, 846 insertions(+) create mode 100644 3.0/modules/albumpassword/controllers/albumpassword.php create mode 100644 3.0/modules/albumpassword/helpers/MY_item.php create mode 100644 3.0/modules/albumpassword/helpers/albumpassword_event.php create mode 100644 3.0/modules/albumpassword/helpers/albumpassword_installer.php create mode 100644 3.0/modules/albumpassword/models/items_albumpassword.php create mode 100644 3.0/modules/albumpassword/module.info create mode 100644 3.0/modules/albumpassword/views/assignpassword.html.php create mode 100644 3.0/modules/albumpassword/views/loginpassword.html.php create mode 100644 3.1/modules/albumpassword/controllers/albumpassword.php create mode 100644 3.1/modules/albumpassword/helpers/MY_item.php create mode 100644 3.1/modules/albumpassword/helpers/albumpassword_event.php create mode 100644 3.1/modules/albumpassword/helpers/albumpassword_installer.php create mode 100644 3.1/modules/albumpassword/models/items_albumpassword.php create mode 100644 3.1/modules/albumpassword/module.info create mode 100644 3.1/modules/albumpassword/views/assignpassword.html.php create mode 100644 3.1/modules/albumpassword/views/loginpassword.html.php diff --git a/3.0/modules/albumpassword/controllers/albumpassword.php b/3.0/modules/albumpassword/controllers/albumpassword.php new file mode 100644 index 00000000..b014b749 --- /dev/null +++ b/3.0/modules/albumpassword/controllers/albumpassword.php @@ -0,0 +1,152 @@ +form = $this->_get_password_form($id); + print $view; + } + + public function login() { + // Display prompt to allow visitors to use their passwords. + + // Create the page. + $view = new View("loginpassword.html"); + $view->form = $this->_get_login_form(); + print $view; + } + + public function remove($id) { + // Remove a password from an album + + // Make sure user has view/edit privileges for this item + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + + // Check for and delete the password. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $id)->find(); + if ($existing_password->loaded()) { + db::build()->delete("items_albumpasswords")->where("album_id", "=", $id)->execute(); + message::success(t("Password Removed.")); + } + + // Redirect the user back to the album. + url::redirect(url::abs_site("albums/" . $id)); + } + + public function savepassword() { + // Save a newly assigned password. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Convert submitted data to local variables. + $album_id = Input::instance()->post("item_id"); + $album_password = Input::instance()->post("assignpassword_password"); + + // Check for, and remove, any existing passwords. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_id)->find(); + if ($existing_password->loaded()) { + db::build()->delete("items_albumpasswords")->where("album_id", "=", $album_id)->execute(); + } + + // Save the new password. + $new_password = ORM::factory("items_albumpassword"); + $new_password->album_id = $album_id; + $new_password->password = $album_password; + $new_password->save(); + + // Display a success message and close the dialog. + message::success(t("Password saved.")); + json::reply(array("result" => "success")); + } + + public function logout() { + // Delete a stored password cookie. + cookie::delete("g3_albumpassword"); + url::redirect(url::abs_site("albums/1")); + } + + public function checkpassword() { + // Check that a password is valid, then store in a browser cookie. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Convert submitted data to local variables. + $album_password = Input::instance()->post("albumpassword_password"); + + // See if the submitted password matches any in the database. + $existing_password = ORM::factory("items_albumpassword") + ->where("password", "=", $album_password) + ->find_all(); + + if (count($existing_password) > 0) { + // If the password if valid, then store it, and display a success message. + // If not, close the dialog and display a rejected message. + cookie::set("g3_albumpassword", $album_password); + message::success(t("Password Accepted.")); + json::reply(array("result" => "success")); + } else { + message::error(t("Password Rejected.")); + json::reply(array("result" => "success")); + } + } + + private function _get_password_form($id) { + // Generate a form for assigning a new password. + $form = new Forge("albumpassword/savepassword", "", "post", + array("id" => "g-assign-password-form")); + $assignpassword_group = $form->group("Enter Password") + ->label(t("Enter Password:")); + $assignpassword_group->hidden("item_id")->value($id); + $assignpassword_group->input("assignpassword_password") + ->id('assignpassword_password') + ->label(t("Password:")); + $form->submit("save_password")->value(t("Save")); + + // Return the newly generated form. + return $form; + } + + private function _get_login_form($id) { + // Generate a form for allowing visitors to enter in their passwords. + $form = new Forge("albumpassword/checkpassword", "", "post", + array("id" => "g-login-password-form")); + $assignpassword_group = $form->group("Enter Password") + ->label(t("Enter Password:")); + $assignpassword_group->input("albumpassword_password") + ->id('albumpassword_password') + ->label(t("Password:")); + $form->submit("login_password")->value(t("Login")); + + // Return the newly generated form. + return $form; + } +} diff --git a/3.0/modules/albumpassword/helpers/MY_item.php b/3.0/modules/albumpassword/helpers/MY_item.php new file mode 100644 index 00000000..3e09a64d --- /dev/null +++ b/3.0/modules/albumpassword/helpers/MY_item.php @@ -0,0 +1,53 @@ +where("id", "=", $model->id)->find(); + + // Figure out if the user can access this album. + $deny_access = false; + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $model->id)->find(); + if ($existing_password->loaded()) { + if ((cookie::get("g3_albumpassword") != $existing_password->password) && + (identity::active_user()->id != $album_item->owner_id)) + $deny_access = true; + } + + // set access::DENY if necessary. + if ($deny_access == true) { + $view_restrictions = array(); + if (!identity::active_user()->admin) { + foreach (identity::group_ids_for_active_user() as $id) { + $view_restrictions[] = array("items.view_$id", "=", access::DENY); + } + } + } + if (count($view_restrictions)) { + $model->and_open()->merge_or_where($view_restrictions)->close(); + } + + return $model; + } +} diff --git a/3.0/modules/albumpassword/helpers/albumpassword_event.php b/3.0/modules/albumpassword/helpers/albumpassword_event.php new file mode 100644 index 00000000..dd83c4d9 --- /dev/null +++ b/3.0/modules/albumpassword/helpers/albumpassword_event.php @@ -0,0 +1,104 @@ +item()) { + return; + } + $item = $theme->item(); + + // If there isn't currently a password stored in the cookie, + // then display the enter password link. + if (cookie::get("g3_albumpassword") == "") { + $menu->append(Menu::factory("dialog") + ->id("albumpassword_login") + ->css_id("g-album-password-login") + ->url(url::site("albumpassword/login")) + ->label(t("Enter password"))); + } else { + // If a password has been entered already + // display the log out link, and links to the protected albums + $menu->append(Menu::factory("submenu") + ->id("albumpassword_protected") + ->css_id("g-album-password-protected") + ->label(t("Protected albums"))); + $menu->get("albumpassword_protected") + ->append(Menu::factory("link") + ->id("albumpassword_logout") + ->css_id("g-album-password-logout") + ->url(url::site("albumpassword/logout")) + ->label(t("Clear password"))); + $existing_password = ORM::factory("items_albumpassword") + ->where("password", "=", cookie::get("g3_albumpassword")) + ->find_all(); + if (count($existing_password) > 0) { + $counter = 0; + while ($counter < count($existing_password)) { + $item_album = ORM::factory("item")->where("id", "=", $existing_password[$counter]->album_id)->find(); + $menu->get("albumpassword_protected") + ->append(Menu::factory("link") + ->id("albumpassword_album" . $counter) + ->label(html::purify($item_album->title)) + ->css_id("g-album-password-album" . $counter) + ->url(url::abs_site("{$item_album->type}s/{$item_album->id}"))); + $counter++; + } + } + } + + // If this is an album without a password, display a link for assigning one. + // If this is an album with a password, display a link to remove it. + if ($item->is_album()) { + if ((access::can("view", $item)) && (access::can("edit", $item))) { + $existing_password = ORM::factory("items_albumpassword") + ->where("album_id", "=", $item->id) + ->find_all(); + if (count($existing_password) > 0) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("albumpassword_remove") + ->label(t("Remove password")) + ->css_id("g-album-password-remove") + ->url(url::site("albumpassword/remove/" . $item->id))); + } else { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("albumpassword_assign") + ->label(t("Assign password")) + ->css_id("g-album-password-assign") + ->url(url::site("albumpassword/assign/" . $item->id))); + } + } + } + } + + static function item_deleted($item) { + // If an album is deleted, remove any associated passwords. + $existingPasswords = ORM::factory("items_albumpassword") + ->where("album_id", "=", $item->id) + ->find_all(); + if (count($existingPasswords) > 0) { + db::build()->delete("items_albumpassword")->where("album_id", "=", $item->id)->execute(); + } + } +} diff --git a/3.0/modules/albumpassword/helpers/albumpassword_installer.php b/3.0/modules/albumpassword/helpers/albumpassword_installer.php new file mode 100644 index 00000000..e59faffb --- /dev/null +++ b/3.0/modules/albumpassword/helpers/albumpassword_installer.php @@ -0,0 +1,42 @@ +query("CREATE TABLE IF NOT EXISTS {items_albumpasswords} ( + `id` int(9) NOT NULL auto_increment, + `album_id` int(9) NOT NULL, + `password` varchar(64) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + + // Set the module's version number. + module::set_version("albumpassword", 1); + } + + static function uninstall() { + // Delete the password table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {items_albumpassword};"); + module::delete("albumpassword"); + } +} diff --git a/3.0/modules/albumpassword/models/items_albumpassword.php b/3.0/modules/albumpassword/models/items_albumpassword.php new file mode 100644 index 00000000..bf0b7341 --- /dev/null +++ b/3.0/modules/albumpassword/models/items_albumpassword.php @@ -0,0 +1,21 @@ + + function ajaxify_login_reset_form() { + $("#g-login form").ajaxForm({ + dataType: "json", + success: function(data) { + if (data.form) { + $("#g-login form").replaceWith(data.form); + ajaxify_login_reset_form(); + } + if (data.result == "success") { + $("#g-dialog").dialog("close"); + window.location.reload(); + } + } + }); + }; + +
    +
      +
    • + +
    • +
    +
    diff --git a/3.0/modules/albumpassword/views/loginpassword.html.php b/3.0/modules/albumpassword/views/loginpassword.html.php new file mode 100644 index 00000000..9ebb47fd --- /dev/null +++ b/3.0/modules/albumpassword/views/loginpassword.html.php @@ -0,0 +1,24 @@ + +
    +
      +
    • + +
    • +
    +
    diff --git a/3.1/modules/albumpassword/controllers/albumpassword.php b/3.1/modules/albumpassword/controllers/albumpassword.php new file mode 100644 index 00000000..b014b749 --- /dev/null +++ b/3.1/modules/albumpassword/controllers/albumpassword.php @@ -0,0 +1,152 @@ +form = $this->_get_password_form($id); + print $view; + } + + public function login() { + // Display prompt to allow visitors to use their passwords. + + // Create the page. + $view = new View("loginpassword.html"); + $view->form = $this->_get_login_form(); + print $view; + } + + public function remove($id) { + // Remove a password from an album + + // Make sure user has view/edit privileges for this item + $item = ORM::factory("item", $id); + access::required("view", $item); + access::required("edit", $item); + + // Check for and delete the password. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $id)->find(); + if ($existing_password->loaded()) { + db::build()->delete("items_albumpasswords")->where("album_id", "=", $id)->execute(); + message::success(t("Password Removed.")); + } + + // Redirect the user back to the album. + url::redirect(url::abs_site("albums/" . $id)); + } + + public function savepassword() { + // Save a newly assigned password. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Convert submitted data to local variables. + $album_id = Input::instance()->post("item_id"); + $album_password = Input::instance()->post("assignpassword_password"); + + // Check for, and remove, any existing passwords. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_id)->find(); + if ($existing_password->loaded()) { + db::build()->delete("items_albumpasswords")->where("album_id", "=", $album_id)->execute(); + } + + // Save the new password. + $new_password = ORM::factory("items_albumpassword"); + $new_password->album_id = $album_id; + $new_password->password = $album_password; + $new_password->save(); + + // Display a success message and close the dialog. + message::success(t("Password saved.")); + json::reply(array("result" => "success")); + } + + public function logout() { + // Delete a stored password cookie. + cookie::delete("g3_albumpassword"); + url::redirect(url::abs_site("albums/1")); + } + + public function checkpassword() { + // Check that a password is valid, then store in a browser cookie. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Convert submitted data to local variables. + $album_password = Input::instance()->post("albumpassword_password"); + + // See if the submitted password matches any in the database. + $existing_password = ORM::factory("items_albumpassword") + ->where("password", "=", $album_password) + ->find_all(); + + if (count($existing_password) > 0) { + // If the password if valid, then store it, and display a success message. + // If not, close the dialog and display a rejected message. + cookie::set("g3_albumpassword", $album_password); + message::success(t("Password Accepted.")); + json::reply(array("result" => "success")); + } else { + message::error(t("Password Rejected.")); + json::reply(array("result" => "success")); + } + } + + private function _get_password_form($id) { + // Generate a form for assigning a new password. + $form = new Forge("albumpassword/savepassword", "", "post", + array("id" => "g-assign-password-form")); + $assignpassword_group = $form->group("Enter Password") + ->label(t("Enter Password:")); + $assignpassword_group->hidden("item_id")->value($id); + $assignpassword_group->input("assignpassword_password") + ->id('assignpassword_password') + ->label(t("Password:")); + $form->submit("save_password")->value(t("Save")); + + // Return the newly generated form. + return $form; + } + + private function _get_login_form($id) { + // Generate a form for allowing visitors to enter in their passwords. + $form = new Forge("albumpassword/checkpassword", "", "post", + array("id" => "g-login-password-form")); + $assignpassword_group = $form->group("Enter Password") + ->label(t("Enter Password:")); + $assignpassword_group->input("albumpassword_password") + ->id('albumpassword_password') + ->label(t("Password:")); + $form->submit("login_password")->value(t("Login")); + + // Return the newly generated form. + return $form; + } +} diff --git a/3.1/modules/albumpassword/helpers/MY_item.php b/3.1/modules/albumpassword/helpers/MY_item.php new file mode 100644 index 00000000..3e09a64d --- /dev/null +++ b/3.1/modules/albumpassword/helpers/MY_item.php @@ -0,0 +1,53 @@ +where("id", "=", $model->id)->find(); + + // Figure out if the user can access this album. + $deny_access = false; + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $model->id)->find(); + if ($existing_password->loaded()) { + if ((cookie::get("g3_albumpassword") != $existing_password->password) && + (identity::active_user()->id != $album_item->owner_id)) + $deny_access = true; + } + + // set access::DENY if necessary. + if ($deny_access == true) { + $view_restrictions = array(); + if (!identity::active_user()->admin) { + foreach (identity::group_ids_for_active_user() as $id) { + $view_restrictions[] = array("items.view_$id", "=", access::DENY); + } + } + } + if (count($view_restrictions)) { + $model->and_open()->merge_or_where($view_restrictions)->close(); + } + + return $model; + } +} diff --git a/3.1/modules/albumpassword/helpers/albumpassword_event.php b/3.1/modules/albumpassword/helpers/albumpassword_event.php new file mode 100644 index 00000000..dd83c4d9 --- /dev/null +++ b/3.1/modules/albumpassword/helpers/albumpassword_event.php @@ -0,0 +1,104 @@ +item()) { + return; + } + $item = $theme->item(); + + // If there isn't currently a password stored in the cookie, + // then display the enter password link. + if (cookie::get("g3_albumpassword") == "") { + $menu->append(Menu::factory("dialog") + ->id("albumpassword_login") + ->css_id("g-album-password-login") + ->url(url::site("albumpassword/login")) + ->label(t("Enter password"))); + } else { + // If a password has been entered already + // display the log out link, and links to the protected albums + $menu->append(Menu::factory("submenu") + ->id("albumpassword_protected") + ->css_id("g-album-password-protected") + ->label(t("Protected albums"))); + $menu->get("albumpassword_protected") + ->append(Menu::factory("link") + ->id("albumpassword_logout") + ->css_id("g-album-password-logout") + ->url(url::site("albumpassword/logout")) + ->label(t("Clear password"))); + $existing_password = ORM::factory("items_albumpassword") + ->where("password", "=", cookie::get("g3_albumpassword")) + ->find_all(); + if (count($existing_password) > 0) { + $counter = 0; + while ($counter < count($existing_password)) { + $item_album = ORM::factory("item")->where("id", "=", $existing_password[$counter]->album_id)->find(); + $menu->get("albumpassword_protected") + ->append(Menu::factory("link") + ->id("albumpassword_album" . $counter) + ->label(html::purify($item_album->title)) + ->css_id("g-album-password-album" . $counter) + ->url(url::abs_site("{$item_album->type}s/{$item_album->id}"))); + $counter++; + } + } + } + + // If this is an album without a password, display a link for assigning one. + // If this is an album with a password, display a link to remove it. + if ($item->is_album()) { + if ((access::can("view", $item)) && (access::can("edit", $item))) { + $existing_password = ORM::factory("items_albumpassword") + ->where("album_id", "=", $item->id) + ->find_all(); + if (count($existing_password) > 0) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("albumpassword_remove") + ->label(t("Remove password")) + ->css_id("g-album-password-remove") + ->url(url::site("albumpassword/remove/" . $item->id))); + } else { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("albumpassword_assign") + ->label(t("Assign password")) + ->css_id("g-album-password-assign") + ->url(url::site("albumpassword/assign/" . $item->id))); + } + } + } + } + + static function item_deleted($item) { + // If an album is deleted, remove any associated passwords. + $existingPasswords = ORM::factory("items_albumpassword") + ->where("album_id", "=", $item->id) + ->find_all(); + if (count($existingPasswords) > 0) { + db::build()->delete("items_albumpassword")->where("album_id", "=", $item->id)->execute(); + } + } +} diff --git a/3.1/modules/albumpassword/helpers/albumpassword_installer.php b/3.1/modules/albumpassword/helpers/albumpassword_installer.php new file mode 100644 index 00000000..e59faffb --- /dev/null +++ b/3.1/modules/albumpassword/helpers/albumpassword_installer.php @@ -0,0 +1,42 @@ +query("CREATE TABLE IF NOT EXISTS {items_albumpasswords} ( + `id` int(9) NOT NULL auto_increment, + `album_id` int(9) NOT NULL, + `password` varchar(64) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + + + // Set the module's version number. + module::set_version("albumpassword", 1); + } + + static function uninstall() { + // Delete the password table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {items_albumpassword};"); + module::delete("albumpassword"); + } +} diff --git a/3.1/modules/albumpassword/models/items_albumpassword.php b/3.1/modules/albumpassword/models/items_albumpassword.php new file mode 100644 index 00000000..bf0b7341 --- /dev/null +++ b/3.1/modules/albumpassword/models/items_albumpassword.php @@ -0,0 +1,21 @@ + + function ajaxify_login_reset_form() { + $("#g-login form").ajaxForm({ + dataType: "json", + success: function(data) { + if (data.form) { + $("#g-login form").replaceWith(data.form); + ajaxify_login_reset_form(); + } + if (data.result == "success") { + $("#g-dialog").dialog("close"); + window.location.reload(); + } + } + }); + }; + +
    +
      +
    • + +
    • +
    +
    diff --git a/3.1/modules/albumpassword/views/loginpassword.html.php b/3.1/modules/albumpassword/views/loginpassword.html.php new file mode 100644 index 00000000..9ebb47fd --- /dev/null +++ b/3.1/modules/albumpassword/views/loginpassword.html.php @@ -0,0 +1,24 @@ + +
    +
      +
    • + +
    • +
    +
    From 77b9ad0737ef7047f370fe65228d02b11b84cd10 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Fri, 29 Oct 2010 02:18:05 -0400 Subject: [PATCH 025/300] CSS Fix. --- 3.0/modules/language_flags/css/language_flags_sidebar.css | 4 ++-- 3.1/modules/language_flags/css/language_flags_sidebar.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3.0/modules/language_flags/css/language_flags_sidebar.css b/3.0/modules/language_flags/css/language_flags_sidebar.css index e532f895..3e6d776b 100644 --- a/3.0/modules/language_flags/css/language_flags_sidebar.css +++ b/3.0/modules/language_flags/css/language_flags_sidebar.css @@ -16,13 +16,13 @@ vertical-align: middle; width: 40px; display: block; margin-left: auto; -margin-right: auto } +margin-right: auto; } #g-selected-language-flag img { width: 48px; display: block; margin-left: auto; -margin-right: auto } +margin-right: auto; } diff --git a/3.1/modules/language_flags/css/language_flags_sidebar.css b/3.1/modules/language_flags/css/language_flags_sidebar.css index e532f895..3e6d776b 100644 --- a/3.1/modules/language_flags/css/language_flags_sidebar.css +++ b/3.1/modules/language_flags/css/language_flags_sidebar.css @@ -16,13 +16,13 @@ vertical-align: middle; width: 40px; display: block; margin-left: auto; -margin-right: auto } +margin-right: auto; } #g-selected-language-flag img { width: 48px; display: block; margin-left: auto; -margin-right: auto } +margin-right: auto; } From 858ab934d41a0f470d03b0503e020bd1e4c18355 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Fri, 29 Oct 2010 02:20:46 -0400 Subject: [PATCH 026/300] W3C compliance fix. --- .../metadescription/views/metadescription_block.html.php | 4 ++-- .../metadescription/views/metadescription_block.html.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3.0/modules/metadescription/views/metadescription_block.html.php b/3.0/modules/metadescription/views/metadescription_block.html.php index 7836beb3..2464b6f1 100644 --- a/3.0/modules/metadescription/views/metadescription_block.html.php +++ b/3.0/modules/metadescription/views/metadescription_block.html.php @@ -41,5 +41,5 @@ // Limit Description to 150 characters. $metaDescription = substr($metaDescription, 0,150); ?> - - + + diff --git a/3.1/modules/metadescription/views/metadescription_block.html.php b/3.1/modules/metadescription/views/metadescription_block.html.php index 7836beb3..2464b6f1 100644 --- a/3.1/modules/metadescription/views/metadescription_block.html.php +++ b/3.1/modules/metadescription/views/metadescription_block.html.php @@ -41,5 +41,5 @@ // Limit Description to 150 characters. $metaDescription = substr($metaDescription, 0,150); ?> - - + + From 5918892314e553309e3f448bbc5649e337ffe825 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Mon, 1 Nov 2010 01:28:59 -0400 Subject: [PATCH 027/300] Bugfixes for older browsers. --- .../albumpassword/controllers/albumpassword.php | 14 ++++++++------ 3.0/modules/albumpassword/helpers/MY_item.php | 2 +- .../albumpassword/views/assignpassword.html.php | 17 ----------------- .../albumpassword/views/loginpassword.html.php | 17 ----------------- .../albumpassword/controllers/albumpassword.php | 14 ++++++++------ 3.1/modules/albumpassword/helpers/MY_item.php | 2 +- .../albumpassword/views/assignpassword.html.php | 17 ----------------- .../albumpassword/views/loginpassword.html.php | 17 ----------------- 8 files changed, 18 insertions(+), 82 deletions(-) diff --git a/3.0/modules/albumpassword/controllers/albumpassword.php b/3.0/modules/albumpassword/controllers/albumpassword.php index b014b749..01080fc2 100644 --- a/3.0/modules/albumpassword/controllers/albumpassword.php +++ b/3.0/modules/albumpassword/controllers/albumpassword.php @@ -84,7 +84,7 @@ class albumpassword_Controller extends Controller { // Display a success message and close the dialog. message::success(t("Password saved.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } public function logout() { @@ -112,10 +112,10 @@ class albumpassword_Controller extends Controller { // If not, close the dialog and display a rejected message. cookie::set("g3_albumpassword", $album_password); message::success(t("Password Accepted.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } else { message::error(t("Password Rejected.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } } @@ -129,7 +129,7 @@ class albumpassword_Controller extends Controller { $assignpassword_group->input("assignpassword_password") ->id('assignpassword_password') ->label(t("Password:")); - $form->submit("save_password")->value(t("Save")); + $assignpassword_group->submit("save_password")->value(t("Save")); // Return the newly generated form. return $form; @@ -139,12 +139,14 @@ class albumpassword_Controller extends Controller { // Generate a form for allowing visitors to enter in their passwords. $form = new Forge("albumpassword/checkpassword", "", "post", array("id" => "g-login-password-form")); + $assignpassword_group = $form->group("Enter Password") ->label(t("Enter Password:")); - $assignpassword_group->input("albumpassword_password") + $assignpassword_group->password("albumpassword_password") ->id('albumpassword_password') ->label(t("Password:")); - $form->submit("login_password")->value(t("Login")); + + $assignpassword_group->submit("")->value(t("Login")); // Return the newly generated form. return $form; diff --git a/3.0/modules/albumpassword/helpers/MY_item.php b/3.0/modules/albumpassword/helpers/MY_item.php index 3e09a64d..e79717c4 100644 --- a/3.0/modules/albumpassword/helpers/MY_item.php +++ b/3.0/modules/albumpassword/helpers/MY_item.php @@ -36,8 +36,8 @@ class item extends item_Core { } // set access::DENY if necessary. + $view_restrictions = array(); if ($deny_access == true) { - $view_restrictions = array(); if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::DENY); diff --git a/3.0/modules/albumpassword/views/assignpassword.html.php b/3.0/modules/albumpassword/views/assignpassword.html.php index 14cd2767..c1a60b8d 100644 --- a/3.0/modules/albumpassword/views/assignpassword.html.php +++ b/3.0/modules/albumpassword/views/assignpassword.html.php @@ -1,20 +1,3 @@ -
    • diff --git a/3.0/modules/albumpassword/views/loginpassword.html.php b/3.0/modules/albumpassword/views/loginpassword.html.php index 9ebb47fd..750ffbed 100644 --- a/3.0/modules/albumpassword/views/loginpassword.html.php +++ b/3.0/modules/albumpassword/views/loginpassword.html.php @@ -1,20 +1,3 @@ -
      • diff --git a/3.1/modules/albumpassword/controllers/albumpassword.php b/3.1/modules/albumpassword/controllers/albumpassword.php index b014b749..01080fc2 100644 --- a/3.1/modules/albumpassword/controllers/albumpassword.php +++ b/3.1/modules/albumpassword/controllers/albumpassword.php @@ -84,7 +84,7 @@ class albumpassword_Controller extends Controller { // Display a success message and close the dialog. message::success(t("Password saved.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } public function logout() { @@ -112,10 +112,10 @@ class albumpassword_Controller extends Controller { // If not, close the dialog and display a rejected message. cookie::set("g3_albumpassword", $album_password); message::success(t("Password Accepted.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } else { message::error(t("Password Rejected.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } } @@ -129,7 +129,7 @@ class albumpassword_Controller extends Controller { $assignpassword_group->input("assignpassword_password") ->id('assignpassword_password') ->label(t("Password:")); - $form->submit("save_password")->value(t("Save")); + $assignpassword_group->submit("save_password")->value(t("Save")); // Return the newly generated form. return $form; @@ -139,12 +139,14 @@ class albumpassword_Controller extends Controller { // Generate a form for allowing visitors to enter in their passwords. $form = new Forge("albumpassword/checkpassword", "", "post", array("id" => "g-login-password-form")); + $assignpassword_group = $form->group("Enter Password") ->label(t("Enter Password:")); - $assignpassword_group->input("albumpassword_password") + $assignpassword_group->password("albumpassword_password") ->id('albumpassword_password') ->label(t("Password:")); - $form->submit("login_password")->value(t("Login")); + + $assignpassword_group->submit("")->value(t("Login")); // Return the newly generated form. return $form; diff --git a/3.1/modules/albumpassword/helpers/MY_item.php b/3.1/modules/albumpassword/helpers/MY_item.php index 3e09a64d..e79717c4 100644 --- a/3.1/modules/albumpassword/helpers/MY_item.php +++ b/3.1/modules/albumpassword/helpers/MY_item.php @@ -36,8 +36,8 @@ class item extends item_Core { } // set access::DENY if necessary. + $view_restrictions = array(); if ($deny_access == true) { - $view_restrictions = array(); if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::DENY); diff --git a/3.1/modules/albumpassword/views/assignpassword.html.php b/3.1/modules/albumpassword/views/assignpassword.html.php index 14cd2767..c1a60b8d 100644 --- a/3.1/modules/albumpassword/views/assignpassword.html.php +++ b/3.1/modules/albumpassword/views/assignpassword.html.php @@ -1,20 +1,3 @@ -
        • diff --git a/3.1/modules/albumpassword/views/loginpassword.html.php b/3.1/modules/albumpassword/views/loginpassword.html.php index 9ebb47fd..750ffbed 100644 --- a/3.1/modules/albumpassword/views/loginpassword.html.php +++ b/3.1/modules/albumpassword/views/loginpassword.html.php @@ -1,20 +1,3 @@ -
          • From be6c8281144706e46dea4438a7d8cf795afda435 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Mon, 1 Nov 2010 01:32:41 -0400 Subject: [PATCH 028/300] Bugfixes for older web browsers. --- .../controllers/albumpassword.php | 24 ------------------- 3.0/modules/albumpassword/helpers/MY_item.php | 5 ---- .../views/assignpassword.html.php | 20 ---------------- .../views/loginpassword.html.php | 20 ---------------- .../controllers/albumpassword.php | 24 ------------------- 3.1/modules/albumpassword/helpers/MY_item.php | 5 ---- .../views/assignpassword.html.php | 20 ---------------- .../views/loginpassword.html.php | 20 ---------------- 8 files changed, 138 deletions(-) diff --git a/3.0/modules/albumpassword/controllers/albumpassword.php b/3.0/modules/albumpassword/controllers/albumpassword.php index 9c672e34..01080fc2 100644 --- a/3.0/modules/albumpassword/controllers/albumpassword.php +++ b/3.0/modules/albumpassword/controllers/albumpassword.php @@ -84,11 +84,7 @@ class albumpassword_Controller extends Controller { // Display a success message and close the dialog. message::success(t("Password saved.")); -<<<<<<< HEAD print "\n\n\n\n\n"; -======= - json::reply(array("result" => "success")); ->>>>>>> gallery3-contrib/master } public function logout() { @@ -116,17 +112,10 @@ class albumpassword_Controller extends Controller { // If not, close the dialog and display a rejected message. cookie::set("g3_albumpassword", $album_password); message::success(t("Password Accepted.")); -<<<<<<< HEAD print "\n\n\n\n\n"; } else { message::error(t("Password Rejected.")); print "\n\n\n\n\n"; -======= - json::reply(array("result" => "success")); - } else { - message::error(t("Password Rejected.")); - json::reply(array("result" => "success")); ->>>>>>> gallery3-contrib/master } } @@ -140,11 +129,7 @@ class albumpassword_Controller extends Controller { $assignpassword_group->input("assignpassword_password") ->id('assignpassword_password') ->label(t("Password:")); -<<<<<<< HEAD $assignpassword_group->submit("save_password")->value(t("Save")); -======= - $form->submit("save_password")->value(t("Save")); ->>>>>>> gallery3-contrib/master // Return the newly generated form. return $form; @@ -154,7 +139,6 @@ class albumpassword_Controller extends Controller { // Generate a form for allowing visitors to enter in their passwords. $form = new Forge("albumpassword/checkpassword", "", "post", array("id" => "g-login-password-form")); -<<<<<<< HEAD $assignpassword_group = $form->group("Enter Password") ->label(t("Enter Password:")); @@ -163,14 +147,6 @@ class albumpassword_Controller extends Controller { ->label(t("Password:")); $assignpassword_group->submit("")->value(t("Login")); -======= - $assignpassword_group = $form->group("Enter Password") - ->label(t("Enter Password:")); - $assignpassword_group->input("albumpassword_password") - ->id('albumpassword_password') - ->label(t("Password:")); - $form->submit("login_password")->value(t("Login")); ->>>>>>> gallery3-contrib/master // Return the newly generated form. return $form; diff --git a/3.0/modules/albumpassword/helpers/MY_item.php b/3.0/modules/albumpassword/helpers/MY_item.php index eb555ef5..e79717c4 100644 --- a/3.0/modules/albumpassword/helpers/MY_item.php +++ b/3.0/modules/albumpassword/helpers/MY_item.php @@ -36,13 +36,8 @@ class item extends item_Core { } // set access::DENY if necessary. -<<<<<<< HEAD $view_restrictions = array(); if ($deny_access == true) { -======= - if ($deny_access == true) { - $view_restrictions = array(); ->>>>>>> gallery3-contrib/master if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::DENY); diff --git a/3.0/modules/albumpassword/views/assignpassword.html.php b/3.0/modules/albumpassword/views/assignpassword.html.php index 43066c68..c1a60b8d 100644 --- a/3.0/modules/albumpassword/views/assignpassword.html.php +++ b/3.0/modules/albumpassword/views/assignpassword.html.php @@ -1,23 +1,3 @@ -<<<<<<< HEAD -======= - ->>>>>>> gallery3-contrib/master
            • diff --git a/3.0/modules/albumpassword/views/loginpassword.html.php b/3.0/modules/albumpassword/views/loginpassword.html.php index cfe8b079..750ffbed 100644 --- a/3.0/modules/albumpassword/views/loginpassword.html.php +++ b/3.0/modules/albumpassword/views/loginpassword.html.php @@ -1,23 +1,3 @@ -<<<<<<< HEAD -======= - ->>>>>>> gallery3-contrib/master
              • diff --git a/3.1/modules/albumpassword/controllers/albumpassword.php b/3.1/modules/albumpassword/controllers/albumpassword.php index 9c672e34..01080fc2 100644 --- a/3.1/modules/albumpassword/controllers/albumpassword.php +++ b/3.1/modules/albumpassword/controllers/albumpassword.php @@ -84,11 +84,7 @@ class albumpassword_Controller extends Controller { // Display a success message and close the dialog. message::success(t("Password saved.")); -<<<<<<< HEAD print "\n\n\n\n\n"; -======= - json::reply(array("result" => "success")); ->>>>>>> gallery3-contrib/master } public function logout() { @@ -116,17 +112,10 @@ class albumpassword_Controller extends Controller { // If not, close the dialog and display a rejected message. cookie::set("g3_albumpassword", $album_password); message::success(t("Password Accepted.")); -<<<<<<< HEAD print "\n\n\n\n\n"; } else { message::error(t("Password Rejected.")); print "\n\n\n\n\n"; -======= - json::reply(array("result" => "success")); - } else { - message::error(t("Password Rejected.")); - json::reply(array("result" => "success")); ->>>>>>> gallery3-contrib/master } } @@ -140,11 +129,7 @@ class albumpassword_Controller extends Controller { $assignpassword_group->input("assignpassword_password") ->id('assignpassword_password') ->label(t("Password:")); -<<<<<<< HEAD $assignpassword_group->submit("save_password")->value(t("Save")); -======= - $form->submit("save_password")->value(t("Save")); ->>>>>>> gallery3-contrib/master // Return the newly generated form. return $form; @@ -154,7 +139,6 @@ class albumpassword_Controller extends Controller { // Generate a form for allowing visitors to enter in their passwords. $form = new Forge("albumpassword/checkpassword", "", "post", array("id" => "g-login-password-form")); -<<<<<<< HEAD $assignpassword_group = $form->group("Enter Password") ->label(t("Enter Password:")); @@ -163,14 +147,6 @@ class albumpassword_Controller extends Controller { ->label(t("Password:")); $assignpassword_group->submit("")->value(t("Login")); -======= - $assignpassword_group = $form->group("Enter Password") - ->label(t("Enter Password:")); - $assignpassword_group->input("albumpassword_password") - ->id('albumpassword_password') - ->label(t("Password:")); - $form->submit("login_password")->value(t("Login")); ->>>>>>> gallery3-contrib/master // Return the newly generated form. return $form; diff --git a/3.1/modules/albumpassword/helpers/MY_item.php b/3.1/modules/albumpassword/helpers/MY_item.php index eb555ef5..e79717c4 100644 --- a/3.1/modules/albumpassword/helpers/MY_item.php +++ b/3.1/modules/albumpassword/helpers/MY_item.php @@ -36,13 +36,8 @@ class item extends item_Core { } // set access::DENY if necessary. -<<<<<<< HEAD $view_restrictions = array(); if ($deny_access == true) { -======= - if ($deny_access == true) { - $view_restrictions = array(); ->>>>>>> gallery3-contrib/master if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::DENY); diff --git a/3.1/modules/albumpassword/views/assignpassword.html.php b/3.1/modules/albumpassword/views/assignpassword.html.php index 43066c68..c1a60b8d 100644 --- a/3.1/modules/albumpassword/views/assignpassword.html.php +++ b/3.1/modules/albumpassword/views/assignpassword.html.php @@ -1,23 +1,3 @@ -<<<<<<< HEAD -======= - ->>>>>>> gallery3-contrib/master
                diff --git a/3.0/themes/sobriety/views/photo.html.php b/3.0/themes/sobriety/views/photo.html.php index c5393547..55b5f4cd 100644 --- a/3.0/themes/sobriety/views/photo.html.php +++ b/3.0/themes/sobriety/views/photo.html.php @@ -4,10 +4,23 @@ From b925e8e97f6be02134578efdfd61fb0c547ecd63 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Mon, 15 Nov 2010 06:29:13 +0800 Subject: [PATCH 062/300] Login menu (probably needs to be polished) --- 3.0/themes/sobriety/css/screen.css | 60 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/3.0/themes/sobriety/css/screen.css b/3.0/themes/sobriety/css/screen.css index ea8bfaf0..09097d56 100644 --- a/3.0/themes/sobriety/css/screen.css +++ b/3.0/themes/sobriety/css/screen.css @@ -60,10 +60,62 @@ div#g-header { border-bottom: 3px solid #bba; } -div#g-banner { +a#g-logo, form#g-quick-search-form, div#g-site-menu { display: none; } +ul#g-login-menu { + list-style-type: none; + + position: absolute; + top: 0; + right: 0; + + display: block; + margin: 0; + /*padding: 0.5em;*/ + padding: 0; + + width: 20em; + width: 2.5em; + height: 2.5em; + background: url('../images/icons/g-login-menu.png') no-repeat center center; + z-index: 2; +} + +ul#g-login-menu > li { + display: none; + text-align: right; + padding: 2px 0; + width: 20em; + margin-left: -17.6em ; /* -(20-2.5) */ + background-color: #fcfcfc; + color: #000; +} + +ul#g-login-menu > li:first-child { + margin-top: 2.4em; +} + +ul#g-login-menu > li > a { + margin-right: 0.5em; +} + +ul#g-login-menu > li:hover, ul#g-login-menu > li:hover > a { + background-color: #5266f3; + color: #fff; + border-color: #fff; /* a:hover */ +} + +ul#g-login-menu:hover { + /*height: auto;*/ + background-color: #5266f3; +} + +ul#g-login-menu:hover > li { + display: block; +} + ul.g-breadcrumbs { margin: 0; padding: 0; @@ -590,7 +642,7 @@ span.ui-icon-key { background-position: -112px -128px; } -#g-login-menu { +/*#g-login-menu { position: absolute; right: 0; padding: 0; @@ -600,11 +652,11 @@ span.ui-icon-key { background-position: -112px -128px; } display: inline; } #g-login-menu li:first-child { - /*display: none;*/ + display: none; } #g-logo, #g-quick-search-form, #g-site-menu { display: none; -} +}*/ From 7139c5c821bb43f55b4a9350e514e5d0af6f12c8 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Mon, 15 Nov 2010 06:29:25 +0800 Subject: [PATCH 063/300] Do not display the bloc 'actions' when there is not link to display (thanks plasticgoat) --- 3.0/themes/sobriety/views/sobriety_actions.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/themes/sobriety/views/sobriety_actions.html.php b/3.0/themes/sobriety/views/sobriety_actions.html.php index a781a558..2338546b 100644 --- a/3.0/themes/sobriety/views/sobriety_actions.html.php +++ b/3.0/themes/sobriety/views/sobriety_actions.html.php @@ -5,7 +5,7 @@ module::event("site_menu", $menu, $this->theme, ""); ?> -elements['add_menu']->elements) || isset($menu->elements['options_menu']->elements) ): ?> +elements['add_menu']->elements) || !empty($menu->elements['options_menu']->elements) ): ?>

                From 73fbf5118fb3ffd2737b25dd37ad4ec41fb1286c Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Wed, 17 Nov 2010 05:37:05 +0800 Subject: [PATCH 064/300] Some cleanup Some bugfixes : - x-repeat -> repeat-x (thanks plasticgoat) - display the frame around image, even when fullsize is not allowed Re-enable show_full_size (it will probably need some theming) Add a style for header text: configure in G3
                Blah
                (thanks plasticgoat) --- 3.0/themes/sobriety/css/screen.css | 29 ++++++++++--------------- 3.0/themes/sobriety/views/page.html.php | 2 +- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/3.0/themes/sobriety/css/screen.css b/3.0/themes/sobriety/css/screen.css index 09097d56..198c0625 100644 --- a/3.0/themes/sobriety/css/screen.css +++ b/3.0/themes/sobriety/css/screen.css @@ -64,6 +64,15 @@ a#g-logo, form#g-quick-search-form, div#g-site-menu { display: none; } +#g-header-text{ + color: #fff; + font: oblique small-caps bold 1em Georgia,serif; + position: absolute; + left: 15px; + height: 1.5em; + line-height: 1.5em; +} + ul#g-login-menu { list-style-type: none; @@ -108,7 +117,6 @@ ul#g-login-menu > li:hover, ul#g-login-menu > li:hover > a { } ul#g-login-menu:hover { - /*height: auto;*/ background-color: #5266f3; } @@ -503,7 +511,7 @@ span.ui-icon-key { background-position: -112px -128px; } border: none; } -#g-item #g-photo a img { +#g-item #g-photo img { border: 10px solid #fff; -webkit-box-shadow: 3px 3px 0px #b7b7a7; -moz-box-shadow: 3px 3px 0px #b7b7a7; @@ -642,21 +650,6 @@ span.ui-icon-key { background-position: -112px -128px; } -/*#g-login-menu { - position: absolute; - right: 0; - padding: 0; - margin: 0; -} -#g-login-menu li { - display: inline; -} -#g-login-menu li:first-child { - display: none; -} -#g-logo, #g-quick-search-form, #g-site-menu { - display: none; -}*/ @@ -863,7 +856,7 @@ div#g-action-status { height: 22px; width: 0%; background-image: url("../images/backgrounds/pbar-ani.gif"); - background-repeat: x-repeat; + background-repeat: repeat-x; } #g-add-photos-progressbar.stop { background-image: url("../images/backgrounds/pbar-ani-stop.gif"); diff --git a/3.0/themes/sobriety/views/page.html.php b/3.0/themes/sobriety/views/page.html.php index a4610460..7458820f 100644 --- a/3.0/themes/sobriety/views/page.html.php +++ b/3.0/themes/sobriety/views/page.html.php @@ -50,7 +50,7 @@ head() they get combined */ ?> page_subtype == "photo"): ?> script("_DISABLED_jquery.scrollTo.js") ?> - script("_DISABLED_gallery.show_full_size.js") ?> + script("gallery.show_full_size.js") ?> page_subtype == "movie"): ?> script("flowplayer.js") ?> From 9ee5582ad4a793e62fa850d294133979f629c5a2 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Fri, 19 Nov 2010 05:43:39 +0800 Subject: [PATCH 065/300] Fix video page --- 3.0/themes/sobriety/css/screen.css | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/3.0/themes/sobriety/css/screen.css b/3.0/themes/sobriety/css/screen.css index 198c0625..7418c954 100644 --- a/3.0/themes/sobriety/css/screen.css +++ b/3.0/themes/sobriety/css/screen.css @@ -503,15 +503,17 @@ span.ui-icon-key { background-position: -112px -128px; } background: #b7b7a7; } -#g-item #g-movie { - display: block; -} - #g-item #g-photo a { border: none; } -#g-item #g-photo img { +#g-item #g-movie, +#g-item #g-movie a { + display: block; +} + +#g-item #g-photo img, +#g-item #g-movie object { border: 10px solid #fff; -webkit-box-shadow: 3px 3px 0px #b7b7a7; -moz-box-shadow: 3px 3px 0px #b7b7a7; From 26f882091b818aa495e6282deac9916efb7fb2ad Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Wed, 24 Nov 2010 06:20:02 +0800 Subject: [PATCH 066/300] [FIX] CRC could be wrong on 32 bits system --- .../downloadalbum/controllers/downloadalbum.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/3.0/modules/downloadalbum/controllers/downloadalbum.php b/3.0/modules/downloadalbum/controllers/downloadalbum.php index 76dd7d8d..73ebd579 100644 --- a/3.0/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.0/modules/downloadalbum/controllers/downloadalbum.php @@ -287,9 +287,13 @@ class downloadalbum_Controller extends Controller { * See http://bugs.php.net/bug.php?id=45028 */ private function fixBug45028($hash) { - return (version_compare(PHP_VERSION, '5.2.7', '<')) - ? (($hash & 0x000000ff) << 24) + (($hash & 0x0000ff00) << 8) - + (($hash & 0x00ff0000) >> 8) + (($hash & 0xff000000) >> 24) - : $hash; + $output = $hash; + + if( version_compare(PHP_VERSION, '5.2.7', '<') ) { + $str = str_pad(dechex($hash), 8, '0', STR_PAD_LEFT); + $output = hexdec($str{6}.$str{7}.$str{4}.$str{5}.$str{2}.$str{3}.$str{0}.$str{1}); + } + + return $output; } } From 5715a6b6850a0977b5b184e72d1851431eaadd12 Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Tue, 23 Nov 2010 05:33:50 +0800 Subject: [PATCH 067/300] added transcode v10 --- .../transcode/controllers/admin_transcode.php | 166 ++++++++++++++ 3.0/modules/transcode/debug.php | 50 +++++ 3.0/modules/transcode/helpers/transcode.php | 59 +++++ .../transcode/helpers/transcode_event.php | 204 ++++++++++++++++++ .../transcode/helpers/transcode_installer.php | 42 ++++ .../transcode/helpers/transcode_task.php | 109 ++++++++++ .../transcode/helpers/transcode_theme.php | 19 ++ .../transcode/models/transcode_resolution.php | 4 + 3.0/modules/transcode/module.info | 3 + .../transcode/views/admin_transcode.html.php | 38 ++++ .../transcode_resolution_variants.html.php | 29 +++ 11 files changed, 723 insertions(+) create mode 100644 3.0/modules/transcode/controllers/admin_transcode.php create mode 100644 3.0/modules/transcode/debug.php create mode 100644 3.0/modules/transcode/helpers/transcode.php create mode 100644 3.0/modules/transcode/helpers/transcode_event.php create mode 100644 3.0/modules/transcode/helpers/transcode_installer.php create mode 100644 3.0/modules/transcode/helpers/transcode_task.php create mode 100644 3.0/modules/transcode/helpers/transcode_theme.php create mode 100644 3.0/modules/transcode/models/transcode_resolution.php create mode 100644 3.0/modules/transcode/module.info create mode 100644 3.0/modules/transcode/views/admin_transcode.html.php create mode 100644 3.0/modules/transcode/views/transcode_resolution_variants.html.php diff --git a/3.0/modules/transcode/controllers/admin_transcode.php b/3.0/modules/transcode/controllers/admin_transcode.php new file mode 100644 index 00000000..b91b1284 --- /dev/null +++ b/3.0/modules/transcode/controllers/admin_transcode.php @@ -0,0 +1,166 @@ + 0) { + module::set_var("transcode", "ffmpeg_path", $_REQUEST['ffmpeg_path']); + $data['success'] = true; + $data['codecs'] = self::_get_supported_audio_codecs($_REQUEST['ffmpeg_path']); + } + else { + $error = ""; + switch ($val) { + case 0: $error = "Empty file path provided"; break; + case -1: $error = "File does not exist"; break; + case -2: $error = "Path is a directory"; break; + default: $error = "Unspecified error"; + } + $data['error'] = $error; + } + echo json_encode($data); + } + + public function index() { + $form = $this->_get_form(); + + if (request::method() == "post") { + access::verify_csrf(); + + if ($form->validate()) { + module::set_var("transcode", "ffmpeg_path", $_POST['ffmpeg_path']); + module::set_var("transcode", "ffmpeg_flags", $_POST['ffmpeg_flags']); + module::set_var("transcode", "audio_codec", $_POST['audio_codec']); + module::set_var("transcode", "ffmpeg_audio_kbits", (isset($_POST['ffmpeg_audio_kbits']) ? $_POST['ffmpeg_audio_kbits'] : false)); + + module::set_var("transcode", "resolution_240p", (isset($_POST['resolution_240p']) ? $_POST['resolution_240p'] : false)); + module::set_var("transcode", "resolution_360p", (isset($_POST['resolution_360p']) ? $_POST['resolution_360p'] : false)); + module::set_var("transcode", "resolution_480p", (isset($_POST['resolution_480p']) ? $_POST['resolution_480p'] : false)); + module::set_var("transcode", "resolution_576p", (isset($_POST['resolution_576p']) ? $_POST['resolution_576p'] : false)); + module::set_var("transcode", "resolution_720p", (isset($_POST['resolution_720p']) ? $_POST['resolution_720p'] : false)); + module::set_var("transcode", "resolution_1080p", (isset($_POST['resolution_1080p']) ? $_POST['resolution_1080p'] : false)); + + message::success(t("Settings have been saved")); + url::redirect("admin/transcode"); + } + else { + message::error(t("There was a problem with the submitted form. Please check your values and try again.")); + } + } + + print $this->_get_view(); + } + + private function _get_view($form = null) { + $v = new Admin_View("admin.html"); + $v->page_title = t("Gallery 3 :: Manage Transcoding Settings"); + + $v->content = new View("admin_transcode.html"); + $v->content->form = empty($form) ? $this->_get_form() : $form; + + return $v; + } + + private function _get_supported_audio_codecs($given_path = null) { + + $flv_compatible_codecs = array("aac" => "aac", "adpcm_swf" => "adpcm_swf", "mp3" => "libmp3lame"); + + if ($given_path) + $ffmpegPath = $given_path; + else + $ffmpegPath = module::get_var("transcode", "ffmpeg_path", transcode::whereis("ffmpeg")); + + $legacy = false; + exec($ffmpegPath . " -codecs", $codecs); + if (count($codecs) == 0) { + $legacy = true; + exec($ffmpegPath . " -formats 2>&1", $codecs); + } + + $search = true; + if ($legacy) $search = false; + + $found = array(); + foreach ($codecs as $line) { + if ($search) { + if (strpos($line, "DEA")) { + $bits = preg_split("/[\s]+/", $line); + if (in_array($bits[2], $flv_compatible_codecs)) { + $key = array_search($bits[2], $flv_compatible_codecs); + $found[$key] = $flv_compatible_codecs[$key]; + } + } + } + + if ($legacy && strpos($line, "Codecs:") !== false) { + $search = true; + continue; + } + if ($legacy && $search && strpos($line, "Bitstream filters:")) { + $search = false; + continue; + } + } + return $found; + } + + private function _get_form() { + $form = new Forge("admin/transcode", "", "post", array("id" => "g-admin-transcode-form")); + + $group = $form->group("system")->label(t("System")); + + $ffmpegPath = transcode::whereis("ffmpeg"); + $codecs = $this->_get_supported_audio_codecs(); + + $group ->input("ffmpeg_path") + ->id("ffmpeg_path") + ->label(t("Path to ffmpeg binary:")) + ->value(module::get_var("transcode", "ffmpeg_path", $ffmpegPath)) + ->callback("transcode::verify_ffmpeg_path") + ->error_messages("required", t("You must enter the path to ffmpeg")) + ->error_messages("invalid", t("File does not exist")) + ->error_messages("is_dir", t("File is a directory")) + ->message("Auto detected ffmpeg here: " . $ffmpegPath . "
                Click here to verify ffmpeg path and continue."); + + $group ->input("ffmpeg_flags") + ->id("ffmpeg_flags") + ->label(t("Extra ffmpeg flags:")) + ->value(module::get_var("transcode", "ffmpeg_flags")); + + $group ->dropdown("audio_codec") + ->id("audio_codec") + ->label(t("Audio codec to use:")) + ->options($codecs) + ->selected(module::get_var("transcode", "audio_codec")); + + $group ->checkbox("ffmpeg_audio_kbits") + ->label(t("Send audio bitrate as kbits instead of bits/s")) + ->checked(module::get_var("transcode", "ffmpeg_audio_kbits")); + + $group = $form->group("resolutions")->label(t("Resolutions")); + + $group ->checkbox("resolution_240p") + ->label("240p") + ->checked(module::get_var("transcode", "resolution_240p")); + $group ->checkbox("resolution_360p") + ->label("360p") + ->checked(module::get_var("transcode", "resolution_360p")); + $group ->checkbox("resolution_480p") + ->label("480p") + ->checked(module::get_var("transcode", "resolution_480p")); + $group ->checkbox("resolution_576p") + ->label("576p") + ->checked(module::get_var("transcode", "resolution_576p")); + $group ->checkbox("resolution_720p") + ->label("720p") + ->checked(module::get_var("transcode", "resolution_720p")); + $group ->checkbox("resolution_1080p") + ->label("1080p") + ->checked(module::get_var("transcode", "resolution_1080p")); + + $form->submit("submit")->value(t("Save")); + return $form; + } +} diff --git a/3.0/modules/transcode/debug.php b/3.0/modules/transcode/debug.php new file mode 100644 index 00000000..7e9d31a9 --- /dev/null +++ b/3.0/modules/transcode/debug.php @@ -0,0 +1,50 @@ +ffmpeg info"; + $ffmpegPath = whereis("ffmpeg"); + echo "path: " . $ffmpegPath . "
                "; + $version = @shell_exec($ffmpegPath . " -version"); $version = explode("\n", $version); $version = $version[0]; $version = explode(" ", $version); $version = $version[1]; + echo "version: " . $version . "
                "; + echo "codecs:
                " . @shell_exec($ffmpegPath . " -codecs 2>&1") . "

                "; + echo "formats:
                " . @shell_exec($ffmpegPath . " -formats") . "

                "; +} + +function whereis($app) { + $op = @shell_exec("whereis " . $app); + if ($op != "") { + $op = explode(" ", $op); + for ($i = 1; $i < count($op); $i++) { + if (file_exists($op[$i]) && !is_dir($op[$i])) + return $op[$i]; + } + } + return false; +} + +/* +stdClass Object +( + [video] => stdClass Object + ( + [codec] => h264, + [height] => 576 + [width] => 640 + ) + + [audio] => stdClass Object + ( + ) + +) + + */ \ No newline at end of file diff --git a/3.0/modules/transcode/helpers/transcode.php b/3.0/modules/transcode/helpers/transcode.php new file mode 100644 index 00000000..f6026ec2 --- /dev/null +++ b/3.0/modules/transcode/helpers/transcode.php @@ -0,0 +1,59 @@ +value); + switch ($v) { + case 0: $field->add_error("required", 1); break; + case -1: $field->add_error("invalid", 1); break; + case -2: $field->add_error("is_dir", 1); break; + } + } + + static function log($item) { + + if (is_string($item) || is_numeric($item)) {} + else + $item = print_r($item, true); + + $fh = fopen(VARPATH . "modules/transcode/log/transcode.log", "a"); + fwrite($fh, date("Y-m-d H:i:s") . ": " . $item . "\n"); + fclose($fh); + + } + +} diff --git a/3.0/modules/transcode/helpers/transcode_event.php b/3.0/modules/transcode/helpers/transcode_event.php new file mode 100644 index 00000000..774f1275 --- /dev/null +++ b/3.0/modules/transcode/helpers/transcode_event.php @@ -0,0 +1,204 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("transcode_menu") + ->label(t("Video Transcoding")) + ->url(url::site("admin/transcode"))); + } + + static function item_deleted($item) { + if ($item->is_movie()) { + transcode::log("Deleting transcoded files for item " . $item->id); + if (is_dir(VARPATH . "modules/transcode/flv/" . $item->id)) { + self::rrmdir(VARPATH . "modules/transcode/flv/" . $item->id); + } + db::build()->delete("transcode_resolutions")->where("item_id", "=", $item->id)->execute(); + } + } + + static function rrmdir($dir) { + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != "." && $object != "..") { + if (filetype($dir."/".$object) == "dir") + self::rrmdir($dir."/".$object); + else + unlink($dir."/".$object); + } + } + reset($objects); + rmdir($dir); + } + } + + private static function _getVideoInfo($file) { + $ffmpegPath = module::get_var("transcode", "ffmpeg_path"); + + $op = array(); + @exec($ffmpegPath . ' -i "' . $file . '" 2>&1', $op); + + $file = new stdclass(); + $file->video = new stdclass(); + $file->audio = new stdclass(); + $file->audio->has = false; + + foreach ($op as $line) { + transcode::log($line); + + if (preg_match('/Duration\: (\d{2}):(\d{2}):(\d{2})\.(\d{2})/', $line, $matches)) { + $file->video->duration = $matches[3] . "." . $matches[4]; + $file->video->duration += $matches[2] * 60; + $file->video->duration += $matches[1] * 3600; + } + else if (preg_match('/Stream #0\.\d(\(\w*\))?\: Video\:/', $line)) { + $bits = preg_split('/[\s]+/', $line); + + for ($i = 0; $i < count($bits); $i++) { + if ($bits[$i] == "fps,") $file->video->fps = $bits[$i - 1]; + else if ($bits[$i] == "kb/s,") $file->video->bitrate = $bits[$i - 1] * 1024; + } + + $file->video->codec = $bits[4]; + list($file->video->width, $file->video->height) = explode('x', $bits[6]); + } + else if (preg_match('/Stream #0\.\d(\(\w*\))?+\: Audio\: (\w*)\,/', $line, $matches)) { + $file->audio->has = true; + $file->audio->codec = $matches[2]; + + if (preg_match('/(\d*) Hz/', $line, $hz)) + $file->audio->samplerate = $hz[1]; + if (preg_match('/(\d*) channels?/', $line, $channels)) + $file->audio->channels = $channels[1]; + if (preg_match('/(\d*) kb\/s/', $line, $bitrate)) + $file->audio->bitrate = $bitrate[1]; + } + } + + return $file; + } + + static function item_created($item) { + if ($item->is_movie()) { + transcode::log("Item created - is a movie. Let's create a transcode task or 2.."); + $ffmpegPath = module::get_var("transcode", "ffmpeg_path"); + + $fileObj = self::_getVideoInfo($item->file_path()); + transcode::log($fileObj); + + // Save our needed variables + $srcFile = $item->file_path(); + $srcWidth = transcode::makeMultipleTwo($fileObj->video->width); + $srcHeight = transcode::makeMultipleTwo($fileObj->video->height); + $aspect = $srcWidth / $srcHeight; + + $srcFPS = $fileObj->video->fps; + + $srcAR = $fileObj->audio->samplerate; + if ($srcAR > 44100) $srcAR = 44100; + + $accepted_sample_rates = array(11025, 22050, 44100); + + if (!in_array($srcAR, $accepted_sample_rates)) { + // if the input sample rate isn't an accepted rate, find the next lowest rate that is + $below = true; $rate = 0; + if ($srcAR < 11025) { + $rate = 11025; + } + else { + foreach ($accepted_sample_rates as $r) { + transcode::log("testing audio rate '" . $r . "' against input rate '" . $srcAR . "'"); + if ($r < $srcAR) { + $rate = $r; + } + } + } + $srcAR = $rate; + } + + $srcACodec = module::get_var("transcode", "audio_codec"); + + $heights = array(); + if (module::get_var("transcode", "resolution_240p")) array_push($heights, 240); + if (module::get_var("transcode", "resolution_360p")) array_push($heights, 360); + if (module::get_var("transcode", "resolution_480p")) array_push($heights, 480); + if (module::get_var("transcode", "resolution_576p")) array_push($heights, 576); + if (module::get_var("transcode", "resolution_720p")) array_push($heights, 720); + if (module::get_var("transcode", "resolution_1080p")) array_push($heights, 1080); + + if (!is_dir(VARPATH . "modules/transcode/flv/" . $item->id)) + @mkdir(VARPATH . "modules/transcode/flv/" . $item->id); + + $xtraFlags = module::get_var("transcode", "ffmpeg_flags", ""); + + foreach ($heights as $destHeight) { + transcode::log("srcHeight: " . $srcHeight . ", destheight: " . $destHeight); + + // don't bother upscaling, there's no advantage to it... + if ($destHeight > $srcHeight) continue; + + $destFormat = module::get_var("transcode", "format", "flv"); + $destWidth = floor($destHeight * $aspect); + if ($destWidth % 2) + $destWidth = ceil($destHeight * $aspect); + + transcode::log("destination resolution: " . $destWidth . "x" . $destHeight); + + $destFile = VARPATH . "modules/transcode/flv/" . $item->id . "/" . $destWidth . "x" . $destHeight . ".flv"; + + switch ($destHeight) { + case 240: $destVB = 128; $srcAB = 16 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break; + case 360: $destVB = 384; $srcAB = 16 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break; + case 480: $destVB = 1024; $srcAB = 32 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break; + case 576: $destVB = 2048; $srcAB = 32 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break; + case 720: $destVB = 4096; $srcAB = 64 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break; + case 1080; $destVB = 8192; $srcAB = 64 * ($fileObj->audio->channels ? $fileObj->audio->channels : 2); break; + } + $destVB *= 1024; + $srcAB *= 1024; + + $cmd = + $ffmpegPath . " " . + "-i \"" . $srcFile . "\" "; + if ($fileObj->audio->has) + $cmd .= + "-y -acodec " . $srcACodec . " " . + "-ar " . $srcAR . " " . + "-ab " . $srcAB . " "; + else + $cmd .= + "-an "; + + $cmd .= + "-vb " . $destVB . " " . + "-f " . $destFormat . " " . + "-s " . $destWidth . "x" . $destHeight . " " . + $xtraFlags . " " . + "\"" . $destFile . "\""; + + transcode::log($cmd); + + $task_def = + Task_Definition::factory() + ->callback("transcode_task::transcode") + ->name("Video Transcode to " . $destWidth . "x" . $destHeight) + ->severity(log::SUCCESS); + + $task = + task::create($task_def, + array("ffmpeg_cmd" => $cmd, + "width" => $destWidth, + "height" => $destHeight, + "item_id" => $item->id) + ); + + task::run($task->id); + } + } + } +} diff --git a/3.0/modules/transcode/helpers/transcode_installer.php b/3.0/modules/transcode/helpers/transcode_installer.php new file mode 100644 index 00000000..6501e4f0 --- /dev/null +++ b/3.0/modules/transcode/helpers/transcode_installer.php @@ -0,0 +1,42 @@ +query("CREATE TABLE IF NOT EXISTS {transcode_resolutions} ( + `id` int(9) NOT NULL auto_increment, + `item_id` int(9) NOT NULL, + `resolution` varchar(16) NOT NULL, + PRIMARY KEY (`id`) + ) DEFAULT CHARSET=utf8;"); + + @mkdir(VARPATH . "modules/transcode"); + @mkdir(VARPATH . "modules/transcode/log"); + @mkdir(VARPATH . "modules/transcode/flv"); + + self::setversion(); + } + static function uninstall() { + Database::instance()->query("DROP TABLE {transcode_resolutions}"); + dir::unlink(VARPATH . "modules/transcode"); + } + + static function upgrade($version) { + if ($version < self::getversion()) + self::setversion(); + } + + static function deactivate() {} + static function activate() {} + static function can_activate() { + $messages = array(); + if (!function_exists("exec")) { + $messages["warn"][] = t("exec() is required to auto-detect the ffmpeg binary. You must specify the path to the ffmpeg binary manually before you can convert videos."); + } + return $messages; + } +} diff --git a/3.0/modules/transcode/helpers/transcode_task.php b/3.0/modules/transcode/helpers/transcode_task.php new file mode 100644 index 00000000..da51bc27 --- /dev/null +++ b/3.0/modules/transcode/helpers/transcode_task.php @@ -0,0 +1,109 @@ +id . " started."); + + $cmd = $task->get("ffmpeg_cmd"); + + $logfile = VARPATH . "modules/transcode/log/" . time() . ".log"; + $task->set("logfile", $logfile); + + $output = ""; + $return_val = 0; + self::fork($cmd, $logfile); + + // wait for ffmpeg to fire up.. + sleep(2); + + while ($time = self::GetEncodedTime($logfile)) { + transcode::log("encoded time: " . $time); + + if (self::$duration > 0) { + $task->state = "encoding"; + $task->status = "Encoding..."; + $pct = sprintf("%0.0f", ($time / self::$duration) * 100); + $task->percent_complete = $pct; + $task->save(); + } + usleep(500000); + } + + $output = @file_get_contents($logfile); + + transcode::log("ffmpeg job completed."); + + if ($output) { + $task->percent_complete = 100; + $task->done = true; + $task->state = "success"; + $task->status = "Transcoding complete."; + + transcode::log("insert into transcode table to indicate success"); + $res = ORM::factory('transcode_resolution'); + $res->resolution = $task->get("width") . "x" . $task->get("height"); + $res->item_id = $task->get("item_id"); + $res->save(); + } + else { + transcode::log("Error transcoding. ffmpeg output:"); + transcode::log($output); + $task->percent_complete = 100; + $task->done = true; + $task->state = "error"; + $task->status = "Transcoding error."; + } + $task->save(); + + } + + static function fork($shellCmd, $logfile) { + $cmd = "nice " . $shellCmd . " > " . $logfile . " 2>&1 &"; + transcode::log("executing: " . $cmd); + exec($cmd); + } + + static function GetEncodedTime($logfile) { + if (!file_exists($logfile)) { + transcode::log("can't open FFMPEG-Log '" . $logfile . "'"); + return false; + } + else { + $FFMPEGLog = @file_get_contents($logfile); + + $dPos = strpos($FFMPEGLog, " Duration: "); + self::$duration = self::durationToSecs(substr($FFMPEGLog, $dPos + 11, 11)); + + $FFMPEGLog = str_replace("\r", "\n", $FFMPEGLog); + + $lines = explode("\n", $FFMPEGLog); + $line = $lines[count($lines) - 2]; + + if ($tpos = strpos($line, "time=")) { + $bpos = strpos($line, " bitrate="); + + $time = substr($line, $tpos + 5, $bpos - ($tpos + 5)); + return $time; + } + else { + return false; + } + } + } + + static function durationToSecs($durstr) { + list($hr, $min, $sec) = explode(":", $durstr); + + $secs = $hr * 3600; + $secs += $min * 60; + $secs += $sec; + + return $secs; + } +} diff --git a/3.0/modules/transcode/helpers/transcode_theme.php b/3.0/modules/transcode/helpers/transcode_theme.php new file mode 100644 index 00000000..371576ac --- /dev/null +++ b/3.0/modules/transcode/helpers/transcode_theme.php @@ -0,0 +1,19 @@ +css_id = "g-resolutions"; + $block->title = t("Alternative Resolutions"); + + $view = new View("transcode_resolution_variants.html"); + $view->item = $theme->item(); + $view->resolutions = ORM::factory("transcode_resolution")->where("item_id", "=", $view->item->id)->find_all(); + + $block->content = $view; + return $block; + } + +} \ No newline at end of file diff --git a/3.0/modules/transcode/models/transcode_resolution.php b/3.0/modules/transcode/models/transcode_resolution.php new file mode 100644 index 00000000..f02cf11c --- /dev/null +++ b/3.0/modules/transcode/models/transcode_resolution.php @@ -0,0 +1,4 @@ + + + +
                +

                + +

                + +
                + +
                +
                + + diff --git a/3.0/modules/transcode/views/transcode_resolution_variants.html.php b/3.0/modules/transcode/views/transcode_resolution_variants.html.php new file mode 100644 index 00000000..6c1b5256 --- /dev/null +++ b/3.0/modules/transcode/views/transcode_resolution_variants.html.php @@ -0,0 +1,29 @@ + + +is_movie()): ?> + 0): ?> + + + +

                No alternative resolutions available.

                + + From a7db07e0f944bb3610d9add6a61ed7262ebab8d5 Mon Sep 17 00:00:00 2001 From: mamouneyya Date: Thu, 4 Nov 2010 19:55:08 +0800 Subject: [PATCH 068/300] - group the font properties together in the file - improved odd and even class display - improved comments display for Comment Block module --- 3.0/themes/browny_wind/css/screen.css | 52 +++++++++++++++++---------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/3.0/themes/browny_wind/css/screen.css b/3.0/themes/browny_wind/css/screen.css index 01576252..ddd593a6 100644 --- a/3.0/themes/browny_wind/css/screen.css +++ b/3.0/themes/browny_wind/css/screen.css @@ -27,6 +27,18 @@ body, html { font-family: 'Century gothic', Verdana, 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; } +.ui-widget { + font-family: 'Century gothic', Verdana, 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; +} + +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { + font-family: 'Century gothic', Verdana, 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; +} + +#g-user-profile #g-comment-detail .g-author { + font-family: Cursive, Serif; +} + p { margin-bottom: 1em; text-shadow: 0px 1px 1px #F7F5F0; @@ -742,13 +754,16 @@ form .g-error { .g-last { } +/* ~browny~ */ .g-even { - background-color: #fff; + background-color: #BAAD8B; + text-align: right; } /* ~browny~ */ .g-odd { background-color: #EDE4D5; + text-align: left; } /** ******************************************************************* @@ -922,14 +937,6 @@ ul.sf-menu li li li.sfHover ul { /* jQuery UI Dialog ~~~~~~~~~~~~~~~~~~~~~~ */ -.ui-widget { - font-family: 'Century gothic', Verdana, 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; -} - -.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { - font-family: 'Century gothic', Verdana, 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; -} - .ui-widget-overlay { background: #000; opacity: .7; @@ -1433,6 +1440,16 @@ div#g-action-status { padding-right: 0; } +/* ~browny~ */ +.rtl .g-even { + text-align: left; +} + +/* ~browny~ */ +.rtl .g-odd { + text-align: right; +} + /** ******************************************************************* * 11) More Browny (Extra overrides for better Browny look) *********************************************************************/ @@ -1497,10 +1514,6 @@ div#g-action-status { margin-bottom: inherit; } -#g-user-profile #g-comment-detail .g-author { - font-family: Cursive, Serif; -} - #g-user-profile #g-comment-detail .g-author a { float: left; position: relative; @@ -1582,12 +1595,6 @@ div#g-action-status { #g-exif-data { font-size: .85em !important; } -.g-odd { - background: #BAAD8B !important; -} -.g-even { - background: #EDE4D5 !important; -} /* 3rd Party Modules ~~~~~~~~~~~~~~~~~~~~~ */ @@ -1620,3 +1627,10 @@ table.calendar td:hover { #g-view-menu #g-download-album-link { background-image: url('../images/ico-view-downloadalbum.png'); } + +/* comment_block */ +.g-odd .g-thumbnail, +.g-even .g-thumbnail { + margin-top: 3px; + margin-bottom: 3px; +} \ No newline at end of file From e8b325878d19786f5a785bd846eca06fde713750 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Mon, 1 Nov 2010 13:28:59 +0800 Subject: [PATCH 069/300] Bugfixes for older browsers. --- .../albumpassword/controllers/albumpassword.php | 14 ++++++++------ 3.0/modules/albumpassword/helpers/MY_item.php | 2 +- .../albumpassword/views/assignpassword.html.php | 17 ----------------- .../albumpassword/views/loginpassword.html.php | 17 ----------------- .../albumpassword/controllers/albumpassword.php | 14 ++++++++------ 3.1/modules/albumpassword/helpers/MY_item.php | 2 +- .../albumpassword/views/assignpassword.html.php | 17 ----------------- .../albumpassword/views/loginpassword.html.php | 17 ----------------- 8 files changed, 18 insertions(+), 82 deletions(-) diff --git a/3.0/modules/albumpassword/controllers/albumpassword.php b/3.0/modules/albumpassword/controllers/albumpassword.php index b014b749..01080fc2 100644 --- a/3.0/modules/albumpassword/controllers/albumpassword.php +++ b/3.0/modules/albumpassword/controllers/albumpassword.php @@ -84,7 +84,7 @@ class albumpassword_Controller extends Controller { // Display a success message and close the dialog. message::success(t("Password saved.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } public function logout() { @@ -112,10 +112,10 @@ class albumpassword_Controller extends Controller { // If not, close the dialog and display a rejected message. cookie::set("g3_albumpassword", $album_password); message::success(t("Password Accepted.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } else { message::error(t("Password Rejected.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } } @@ -129,7 +129,7 @@ class albumpassword_Controller extends Controller { $assignpassword_group->input("assignpassword_password") ->id('assignpassword_password') ->label(t("Password:")); - $form->submit("save_password")->value(t("Save")); + $assignpassword_group->submit("save_password")->value(t("Save")); // Return the newly generated form. return $form; @@ -139,12 +139,14 @@ class albumpassword_Controller extends Controller { // Generate a form for allowing visitors to enter in their passwords. $form = new Forge("albumpassword/checkpassword", "", "post", array("id" => "g-login-password-form")); + $assignpassword_group = $form->group("Enter Password") ->label(t("Enter Password:")); - $assignpassword_group->input("albumpassword_password") + $assignpassword_group->password("albumpassword_password") ->id('albumpassword_password') ->label(t("Password:")); - $form->submit("login_password")->value(t("Login")); + + $assignpassword_group->submit("")->value(t("Login")); // Return the newly generated form. return $form; diff --git a/3.0/modules/albumpassword/helpers/MY_item.php b/3.0/modules/albumpassword/helpers/MY_item.php index 3e09a64d..e79717c4 100644 --- a/3.0/modules/albumpassword/helpers/MY_item.php +++ b/3.0/modules/albumpassword/helpers/MY_item.php @@ -36,8 +36,8 @@ class item extends item_Core { } // set access::DENY if necessary. + $view_restrictions = array(); if ($deny_access == true) { - $view_restrictions = array(); if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::DENY); diff --git a/3.0/modules/albumpassword/views/assignpassword.html.php b/3.0/modules/albumpassword/views/assignpassword.html.php index 14cd2767..c1a60b8d 100644 --- a/3.0/modules/albumpassword/views/assignpassword.html.php +++ b/3.0/modules/albumpassword/views/assignpassword.html.php @@ -1,20 +1,3 @@ -
                • diff --git a/3.0/modules/albumpassword/views/loginpassword.html.php b/3.0/modules/albumpassword/views/loginpassword.html.php index 9ebb47fd..750ffbed 100644 --- a/3.0/modules/albumpassword/views/loginpassword.html.php +++ b/3.0/modules/albumpassword/views/loginpassword.html.php @@ -1,20 +1,3 @@ -
                  • diff --git a/3.1/modules/albumpassword/controllers/albumpassword.php b/3.1/modules/albumpassword/controllers/albumpassword.php index b014b749..01080fc2 100644 --- a/3.1/modules/albumpassword/controllers/albumpassword.php +++ b/3.1/modules/albumpassword/controllers/albumpassword.php @@ -84,7 +84,7 @@ class albumpassword_Controller extends Controller { // Display a success message and close the dialog. message::success(t("Password saved.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } public function logout() { @@ -112,10 +112,10 @@ class albumpassword_Controller extends Controller { // If not, close the dialog and display a rejected message. cookie::set("g3_albumpassword", $album_password); message::success(t("Password Accepted.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } else { message::error(t("Password Rejected.")); - json::reply(array("result" => "success")); + print "\n\n\n\n\n"; } } @@ -129,7 +129,7 @@ class albumpassword_Controller extends Controller { $assignpassword_group->input("assignpassword_password") ->id('assignpassword_password') ->label(t("Password:")); - $form->submit("save_password")->value(t("Save")); + $assignpassword_group->submit("save_password")->value(t("Save")); // Return the newly generated form. return $form; @@ -139,12 +139,14 @@ class albumpassword_Controller extends Controller { // Generate a form for allowing visitors to enter in their passwords. $form = new Forge("albumpassword/checkpassword", "", "post", array("id" => "g-login-password-form")); + $assignpassword_group = $form->group("Enter Password") ->label(t("Enter Password:")); - $assignpassword_group->input("albumpassword_password") + $assignpassword_group->password("albumpassword_password") ->id('albumpassword_password') ->label(t("Password:")); - $form->submit("login_password")->value(t("Login")); + + $assignpassword_group->submit("")->value(t("Login")); // Return the newly generated form. return $form; diff --git a/3.1/modules/albumpassword/helpers/MY_item.php b/3.1/modules/albumpassword/helpers/MY_item.php index 3e09a64d..e79717c4 100644 --- a/3.1/modules/albumpassword/helpers/MY_item.php +++ b/3.1/modules/albumpassword/helpers/MY_item.php @@ -36,8 +36,8 @@ class item extends item_Core { } // set access::DENY if necessary. + $view_restrictions = array(); if ($deny_access == true) { - $view_restrictions = array(); if (!identity::active_user()->admin) { foreach (identity::group_ids_for_active_user() as $id) { $view_restrictions[] = array("items.view_$id", "=", access::DENY); diff --git a/3.1/modules/albumpassword/views/assignpassword.html.php b/3.1/modules/albumpassword/views/assignpassword.html.php index 14cd2767..c1a60b8d 100644 --- a/3.1/modules/albumpassword/views/assignpassword.html.php +++ b/3.1/modules/albumpassword/views/assignpassword.html.php @@ -1,20 +1,3 @@ -
                    • diff --git a/3.1/modules/albumpassword/views/loginpassword.html.php b/3.1/modules/albumpassword/views/loginpassword.html.php index 9ebb47fd..750ffbed 100644 --- a/3.1/modules/albumpassword/views/loginpassword.html.php +++ b/3.1/modules/albumpassword/views/loginpassword.html.php @@ -1,20 +1,3 @@ -
                      • From 5100e8123a9e1cda670a097b2dac9baf35f7568e Mon Sep 17 00:00:00 2001 From: rWatcher Date: Mon, 1 Nov 2010 13:43:12 +0800 Subject: [PATCH 070/300] CSS Fix. --- .../css/language_flags_sidebar.css | 38 ++++++++----------- .../css/language_flags_sidebar.css | 38 ++++++++----------- 2 files changed, 30 insertions(+), 46 deletions(-) diff --git a/3.0/modules/language_flags/css/language_flags_sidebar.css b/3.0/modules/language_flags/css/language_flags_sidebar.css index 3e6d776b..705dad2d 100644 --- a/3.0/modules/language_flags/css/language_flags_sidebar.css +++ b/3.0/modules/language_flags/css/language_flags_sidebar.css @@ -1,28 +1,20 @@ -/* Grid ****************************** */ -#g-selected-language-flag, #g-language-flag, #g-default-language-flag { -position: relative; -float: left; -margin: 2px 2px 2px 2px; -width: 50px; -height: 50px; -display: table-cell; -vertical-align: middle; -/* border:1px solid #fff; */ +/* Flag container divs */ +#g-selected-language-flag, +#g-language-flag, +#g-default-language-flag { + display: inline; + margin: 2px; } -/* Resize ****************************** */ - -#g-language-flag img, #g-default-language-flag img { -width: 40px; -display: block; -margin-left: auto; -margin-right: auto; +/* Flags with standard size */ +#g-language-flag img, +#g-default-language-flag img { + width: 40px; + margin: 5px; } +/* Flag grows when selected */ #g-selected-language-flag img { -width: 48px; -display: block; -margin-left: auto; -margin-right: auto; -} - + width: 48px; + margin: 1px; +} \ No newline at end of file diff --git a/3.1/modules/language_flags/css/language_flags_sidebar.css b/3.1/modules/language_flags/css/language_flags_sidebar.css index 3e6d776b..705dad2d 100644 --- a/3.1/modules/language_flags/css/language_flags_sidebar.css +++ b/3.1/modules/language_flags/css/language_flags_sidebar.css @@ -1,28 +1,20 @@ -/* Grid ****************************** */ -#g-selected-language-flag, #g-language-flag, #g-default-language-flag { -position: relative; -float: left; -margin: 2px 2px 2px 2px; -width: 50px; -height: 50px; -display: table-cell; -vertical-align: middle; -/* border:1px solid #fff; */ +/* Flag container divs */ +#g-selected-language-flag, +#g-language-flag, +#g-default-language-flag { + display: inline; + margin: 2px; } -/* Resize ****************************** */ - -#g-language-flag img, #g-default-language-flag img { -width: 40px; -display: block; -margin-left: auto; -margin-right: auto; +/* Flags with standard size */ +#g-language-flag img, +#g-default-language-flag img { + width: 40px; + margin: 5px; } +/* Flag grows when selected */ #g-selected-language-flag img { -width: 48px; -display: block; -margin-left: auto; -margin-right: auto; -} - + width: 48px; + margin: 1px; +} \ No newline at end of file From 99697342861d17c673659be7b634f4f0946e4b85 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sat, 6 Nov 2010 13:34:17 +0800 Subject: [PATCH 071/300] Fix for full screen aspect ratio bug (Ticket #1154). --- 3.0/modules/videos/views/movieplayer.html.php | 3 +++ 3.1/modules/videos/views/movieplayer.html.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/3.0/modules/videos/views/movieplayer.html.php b/3.0/modules/videos/views/movieplayer.html.php index b960e1d7..1cf46cda 100644 --- a/3.0/modules/videos/views/movieplayer.html.php +++ b/3.0/modules/videos/views/movieplayer.html.php @@ -19,6 +19,9 @@ provider: "pseudostreaming" }, { + clip: { + scaling: 'fit' + }, plugins: { pseudostreaming: { url: "" diff --git a/3.1/modules/videos/views/movieplayer.html.php b/3.1/modules/videos/views/movieplayer.html.php index b960e1d7..1cf46cda 100644 --- a/3.1/modules/videos/views/movieplayer.html.php +++ b/3.1/modules/videos/views/movieplayer.html.php @@ -19,6 +19,9 @@ provider: "pseudostreaming" }, { + clip: { + scaling: 'fit' + }, plugins: { pseudostreaming: { url: "" From 63873f96cd78f189773c72046feaad1fe6d83580 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 12:20:50 +0800 Subject: [PATCH 072/300] Hide the album instead of the albums contents. --- 3.0/modules/albumpassword/helpers/MY_item.php | 33 ++++++------------- 3.1/modules/albumpassword/helpers/MY_item.php | 33 ++++++------------- 2 files changed, 20 insertions(+), 46 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/MY_item.php b/3.0/modules/albumpassword/helpers/MY_item.php index e79717c4..79dd3afc 100644 --- a/3.0/modules/albumpassword/helpers/MY_item.php +++ b/3.0/modules/albumpassword/helpers/MY_item.php @@ -20,32 +20,19 @@ class item extends item_Core { static function viewable($model) { - // Hide the contents of a password protected album, - // Unless the current user is an admin, or the albums owner. + // Hide password protected albums until the correct password is entered, + // unless the current user is an admin, or the albums owner. $model = item_Core::viewable($model); - $album_item = ORM::factory("item")->where("id", "=", $model->id)->find(); - // Figure out if the user can access this album. - $deny_access = false; - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $model->id)->find(); - if ($existing_password->loaded()) { - if ((cookie::get("g3_albumpassword") != $existing_password->password) && - (identity::active_user()->id != $album_item->owner_id)) - $deny_access = true; - } - - // set access::DENY if necessary. - $view_restrictions = array(); - if ($deny_access == true) { - if (!identity::active_user()->admin) { - foreach (identity::group_ids_for_active_user() as $id) { - $view_restrictions[] = array("items.view_$id", "=", access::DENY); - } - } - } - if (count($view_restrictions)) { - $model->and_open()->merge_or_where($view_restrictions)->close(); + // If the user is an admin, don't hide anything anything. + // If not, hide whatever is restricted by an album password + // that the current user is not the owner of. + if (!identity::active_user()->admin) { + $model->and_open()->join("items_albumpasswords", "items.id", "items_albumpasswords.album_id", "LEFT OUTER") + ->and_where("items_albumpasswords.album_id", "IS", NULL) + ->or_where("items_albumpasswords.password", "=", cookie::get("g3_albumpassword")) + ->or_where("items.owner_id", "=", identity::active_user()->id)->close(); } return $model; diff --git a/3.1/modules/albumpassword/helpers/MY_item.php b/3.1/modules/albumpassword/helpers/MY_item.php index e79717c4..79dd3afc 100644 --- a/3.1/modules/albumpassword/helpers/MY_item.php +++ b/3.1/modules/albumpassword/helpers/MY_item.php @@ -20,32 +20,19 @@ class item extends item_Core { static function viewable($model) { - // Hide the contents of a password protected album, - // Unless the current user is an admin, or the albums owner. + // Hide password protected albums until the correct password is entered, + // unless the current user is an admin, or the albums owner. $model = item_Core::viewable($model); - $album_item = ORM::factory("item")->where("id", "=", $model->id)->find(); - // Figure out if the user can access this album. - $deny_access = false; - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $model->id)->find(); - if ($existing_password->loaded()) { - if ((cookie::get("g3_albumpassword") != $existing_password->password) && - (identity::active_user()->id != $album_item->owner_id)) - $deny_access = true; - } - - // set access::DENY if necessary. - $view_restrictions = array(); - if ($deny_access == true) { - if (!identity::active_user()->admin) { - foreach (identity::group_ids_for_active_user() as $id) { - $view_restrictions[] = array("items.view_$id", "=", access::DENY); - } - } - } - if (count($view_restrictions)) { - $model->and_open()->merge_or_where($view_restrictions)->close(); + // If the user is an admin, don't hide anything anything. + // If not, hide whatever is restricted by an album password + // that the current user is not the owner of. + if (!identity::active_user()->admin) { + $model->and_open()->join("items_albumpasswords", "items.id", "items_albumpasswords.album_id", "LEFT OUTER") + ->and_where("items_albumpasswords.album_id", "IS", NULL) + ->or_where("items_albumpasswords.password", "=", cookie::get("g3_albumpassword")) + ->or_where("items.owner_id", "=", identity::active_user()->id)->close(); } return $model; From 73ebd81bc1f5453d5355a13540475719970aa14d Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:10:01 +0800 Subject: [PATCH 073/300] Deny access to protected albums and their contents. --- .../albumpassword/helpers/MY_access.php | 57 +++++++++++++++++++ .../albumpassword/helpers/MY_access.php | 57 +++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 3.0/modules/albumpassword/helpers/MY_access.php create mode 100644 3.1/modules/albumpassword/helpers/MY_access.php diff --git a/3.0/modules/albumpassword/helpers/MY_access.php b/3.0/modules/albumpassword/helpers/MY_access.php new file mode 100644 index 00000000..65052c8a --- /dev/null +++ b/3.0/modules/albumpassword/helpers/MY_access.php @@ -0,0 +1,57 @@ +is_album()) { + $album_item = $item; + } else { + $album_item = $item->parent(); + } + } else { + $album_item = $album_item->parent(); + } + + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_item->id)->find(); + if ($existing_password->loaded()) { + if ((cookie::get("g3_albumpassword") != $existing_password->password) && + (identity::active_user()->id != $album_item->owner_id)) { + throw new Kohana_404_Exception(); + } + } + } while ($album_item->parent_id > 0); + } + } +} diff --git a/3.1/modules/albumpassword/helpers/MY_access.php b/3.1/modules/albumpassword/helpers/MY_access.php new file mode 100644 index 00000000..65052c8a --- /dev/null +++ b/3.1/modules/albumpassword/helpers/MY_access.php @@ -0,0 +1,57 @@ +is_album()) { + $album_item = $item; + } else { + $album_item = $item->parent(); + } + } else { + $album_item = $album_item->parent(); + } + + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_item->id)->find(); + if ($existing_password->loaded()) { + if ((cookie::get("g3_albumpassword") != $existing_password->password) && + (identity::active_user()->id != $album_item->owner_id)) { + throw new Kohana_404_Exception(); + } + } + } while ($album_item->parent_id > 0); + } + } +} From 9c7db084ee1c4aa310fc14a1dfbae3b7fb55330c Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:10:48 +0800 Subject: [PATCH 074/300] Increase version number two 2, create a new module variable. --- .../helpers/albumpassword_installer.php | 14 ++++++++++++-- 3.0/modules/albumpassword/module.info | 2 +- .../helpers/albumpassword_installer.php | 14 ++++++++++++-- 3.1/modules/albumpassword/module.info | 2 +- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/albumpassword_installer.php b/3.0/modules/albumpassword/helpers/albumpassword_installer.php index e59faffb..42af0ddf 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_installer.php @@ -28,9 +28,19 @@ class albumpassword_installer { PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); - + // Set the default value for this module's behavior. + module::set_var("albumpassword", "hideonly", true); + // Set the module's version number. - module::set_version("albumpassword", 1); + module::set_version("albumpassword", 2); + } + + static function upgrade($version) { + // Set the default value for this module's behavior. + module::set_var("albumpassword", "hideonly", true); + + // Set the module's version number. + module::set_version("albumpassword", 2); } static function uninstall() { diff --git a/3.0/modules/albumpassword/module.info b/3.0/modules/albumpassword/module.info index 6acdc1dd..cd1262f4 100644 --- a/3.0/modules/albumpassword/module.info +++ b/3.0/modules/albumpassword/module.info @@ -1,3 +1,3 @@ name = "Album Password" description = "Restrict access to individual albums." -version = 1 +version = 2 diff --git a/3.1/modules/albumpassword/helpers/albumpassword_installer.php b/3.1/modules/albumpassword/helpers/albumpassword_installer.php index e59faffb..42af0ddf 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_installer.php @@ -28,9 +28,19 @@ class albumpassword_installer { PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); - + // Set the default value for this module's behavior. + module::set_var("albumpassword", "hideonly", true); + // Set the module's version number. - module::set_version("albumpassword", 1); + module::set_version("albumpassword", 2); + } + + static function upgrade($version) { + // Set the default value for this module's behavior. + module::set_var("albumpassword", "hideonly", true); + + // Set the module's version number. + module::set_version("albumpassword", 2); } static function uninstall() { diff --git a/3.1/modules/albumpassword/module.info b/3.1/modules/albumpassword/module.info index 6acdc1dd..cd1262f4 100644 --- a/3.1/modules/albumpassword/module.info +++ b/3.1/modules/albumpassword/module.info @@ -1,3 +1,3 @@ name = "Album Password" description = "Restrict access to individual albums." -version = 1 +version = 2 From e09864291e77400f45969dce87038241bdf3d9fd Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:11:13 +0800 Subject: [PATCH 075/300] Don't allow users to protect the root album. --- 3.0/modules/albumpassword/helpers/albumpassword_event.php | 2 +- 3.1/modules/albumpassword/helpers/albumpassword_event.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/albumpassword_event.php b/3.0/modules/albumpassword/helpers/albumpassword_event.php index dd83c4d9..43747584 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_event.php @@ -80,7 +80,7 @@ class albumpassword_event_Core { ->label(t("Remove password")) ->css_id("g-album-password-remove") ->url(url::site("albumpassword/remove/" . $item->id))); - } else { + } elseif ($item->id != 1) { $menu->get("options_menu") ->append(Menu::factory("dialog") ->id("albumpassword_assign") diff --git a/3.1/modules/albumpassword/helpers/albumpassword_event.php b/3.1/modules/albumpassword/helpers/albumpassword_event.php index dd83c4d9..43747584 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_event.php @@ -80,7 +80,7 @@ class albumpassword_event_Core { ->label(t("Remove password")) ->css_id("g-album-password-remove") ->url(url::site("albumpassword/remove/" . $item->id))); - } else { + } elseif ($item->id != 1) { $menu->get("options_menu") ->append(Menu::factory("dialog") ->id("albumpassword_assign") From 9f981b56c330b713b10eba5a4713b02f5bc350b8 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:38:57 +0800 Subject: [PATCH 076/300] Created an admin screen. --- .../albumpassword/helpers/albumpassword_event.php | 9 +++++++++ .../albumpassword/helpers/albumpassword_event.php | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/3.0/modules/albumpassword/helpers/albumpassword_event.php b/3.0/modules/albumpassword/helpers/albumpassword_event.php index 43747584..c5ead56a 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_event.php @@ -101,4 +101,13 @@ class albumpassword_event_Core { db::build()->delete("items_albumpassword")->where("album_id", "=", $item->id)->execute(); } } + + static function admin_menu($menu, $theme) { + // Add a link to the Album Password admin page to the Content menu. + $menu->get("settings_menu") + ->append(Menu::factory("link") + ->id("albumpassword") + ->label(t("Album Password Settings")) + ->url(url::site("admin/albumpassword"))); + } } diff --git a/3.1/modules/albumpassword/helpers/albumpassword_event.php b/3.1/modules/albumpassword/helpers/albumpassword_event.php index 43747584..c5ead56a 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_event.php @@ -101,4 +101,13 @@ class albumpassword_event_Core { db::build()->delete("items_albumpassword")->where("album_id", "=", $item->id)->execute(); } } + + static function admin_menu($menu, $theme) { + // Add a link to the Album Password admin page to the Content menu. + $menu->get("settings_menu") + ->append(Menu::factory("link") + ->id("albumpassword") + ->label(t("Album Password Settings")) + ->url(url::site("admin/albumpassword"))); + } } From 26a2d9c0f980c744ad4248ce47a9135227b17d5f Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:43:09 +0800 Subject: [PATCH 077/300] Modify access to actually use the admin setting. --- 3.0/modules/albumpassword/helpers/MY_access.php | 4 ++-- 3.1/modules/albumpassword/helpers/MY_access.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/MY_access.php b/3.0/modules/albumpassword/helpers/MY_access.php index 65052c8a..55f85f33 100644 --- a/3.0/modules/albumpassword/helpers/MY_access.php +++ b/3.0/modules/albumpassword/helpers/MY_access.php @@ -31,7 +31,7 @@ class access extends access_Core { // Begin rWatcher modifications. // This section adds an additional condition onto the view permission that throws a 404 // error if the album has a password assigned. - } elseif ($perm_name == "view") { + } elseif (($perm_name == "view") && (module::get_var("albumpassword", "hideonly") == false)) { $album_item = ""; do { if ($album_item == "") { @@ -43,7 +43,7 @@ class access extends access_Core { } else { $album_item = $album_item->parent(); } - + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_item->id)->find(); if ($existing_password->loaded()) { if ((cookie::get("g3_albumpassword") != $existing_password->password) && diff --git a/3.1/modules/albumpassword/helpers/MY_access.php b/3.1/modules/albumpassword/helpers/MY_access.php index 65052c8a..55f85f33 100644 --- a/3.1/modules/albumpassword/helpers/MY_access.php +++ b/3.1/modules/albumpassword/helpers/MY_access.php @@ -31,7 +31,7 @@ class access extends access_Core { // Begin rWatcher modifications. // This section adds an additional condition onto the view permission that throws a 404 // error if the album has a password assigned. - } elseif ($perm_name == "view") { + } elseif (($perm_name == "view") && (module::get_var("albumpassword", "hideonly") == false)) { $album_item = ""; do { if ($album_item == "") { @@ -43,7 +43,7 @@ class access extends access_Core { } else { $album_item = $album_item->parent(); } - + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_item->id)->find(); if ($existing_password->loaded()) { if ((cookie::get("g3_albumpassword") != $existing_password->password) && From a77876ec00d53ba6b99027c9a9586eb9866510c7 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:44:07 +0800 Subject: [PATCH 078/300] Create an admin page. --- .../controllers/admin_albumpassword.php | 69 +++++++++++++++++++ .../views/admin_albumpassword.html.php | 9 +++ .../controllers/admin_albumpassword.php | 69 +++++++++++++++++++ .../views/admin_albumpassword.html.php | 9 +++ 4 files changed, 156 insertions(+) create mode 100644 3.0/modules/albumpassword/controllers/admin_albumpassword.php create mode 100644 3.0/modules/albumpassword/views/admin_albumpassword.html.php create mode 100644 3.1/modules/albumpassword/controllers/admin_albumpassword.php create mode 100644 3.1/modules/albumpassword/views/admin_albumpassword.html.php diff --git a/3.0/modules/albumpassword/controllers/admin_albumpassword.php b/3.0/modules/albumpassword/controllers/admin_albumpassword.php new file mode 100644 index 00000000..db134c66 --- /dev/null +++ b/3.0/modules/albumpassword/controllers/admin_albumpassword.php @@ -0,0 +1,69 @@ +content = new View("admin_albumpassword.html"); + + // Generate a form for controlling the admin section. + $view->content->albumpassword_form = $this->_get_admin_form(); + + // Display the page. + print $view; + } + + private function _get_admin_form() { + // Make a new form for changing admin settings for this module. + $form = new Forge("admin/albumpassword/saveprefs", "", "post", + array("id" => "g-album-password-admin-form")); + + // Should protected items be hidden, or completely in-accessable? + $albumpassword_group = $form->group("album_password_group"); + $albumpassword_group->checkbox("hideonly") + ->label("Only hide protected albums?") + ->checked(module::get_var("albumpassword", "hideonly")); + + // Add a save button to the form. + $albumpassword_group->submit("save_settings")->value(t("Save")); + + // Return the newly generated form. + return $form; + } + + public function saveprefs() { + // Save user specified preferences. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Retrieve submitted form data. + if (Input::instance()->post("hideonly") == false) { + module::set_var("albumpassword", "hideonly", false); + } else { + module::set_var("albumpassword", "hideonly", true); + } + // Display a success message and redirect back to the TagsMap admin page. + message::success(t("Your settings have been saved.")); + url::redirect("admin/albumpassword"); + } +} \ No newline at end of file diff --git a/3.0/modules/albumpassword/views/admin_albumpassword.html.php b/3.0/modules/albumpassword/views/admin_albumpassword.html.php new file mode 100644 index 00000000..05e46454 --- /dev/null +++ b/3.0/modules/albumpassword/views/admin_albumpassword.html.php @@ -0,0 +1,9 @@ + +

                        + +

                        +
                        +
                        +

                        + +
                        diff --git a/3.1/modules/albumpassword/controllers/admin_albumpassword.php b/3.1/modules/albumpassword/controllers/admin_albumpassword.php new file mode 100644 index 00000000..db134c66 --- /dev/null +++ b/3.1/modules/albumpassword/controllers/admin_albumpassword.php @@ -0,0 +1,69 @@ +content = new View("admin_albumpassword.html"); + + // Generate a form for controlling the admin section. + $view->content->albumpassword_form = $this->_get_admin_form(); + + // Display the page. + print $view; + } + + private function _get_admin_form() { + // Make a new form for changing admin settings for this module. + $form = new Forge("admin/albumpassword/saveprefs", "", "post", + array("id" => "g-album-password-admin-form")); + + // Should protected items be hidden, or completely in-accessable? + $albumpassword_group = $form->group("album_password_group"); + $albumpassword_group->checkbox("hideonly") + ->label("Only hide protected albums?") + ->checked(module::get_var("albumpassword", "hideonly")); + + // Add a save button to the form. + $albumpassword_group->submit("save_settings")->value(t("Save")); + + // Return the newly generated form. + return $form; + } + + public function saveprefs() { + // Save user specified preferences. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + // Retrieve submitted form data. + if (Input::instance()->post("hideonly") == false) { + module::set_var("albumpassword", "hideonly", false); + } else { + module::set_var("albumpassword", "hideonly", true); + } + // Display a success message and redirect back to the TagsMap admin page. + message::success(t("Your settings have been saved.")); + url::redirect("admin/albumpassword"); + } +} \ No newline at end of file diff --git a/3.1/modules/albumpassword/views/admin_albumpassword.html.php b/3.1/modules/albumpassword/views/admin_albumpassword.html.php new file mode 100644 index 00000000..05e46454 --- /dev/null +++ b/3.1/modules/albumpassword/views/admin_albumpassword.html.php @@ -0,0 +1,9 @@ + +

                        + +

                        +
                        +
                        +

                        + +
                        From 06725f6ce1285093ca042aed38ca301399ada6f2 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 7 Nov 2010 13:48:21 +0800 Subject: [PATCH 079/300] Cleaning up the code. --- .../controllers/admin_albumpassword.php | 16 ++++++++-------- 3.0/modules/albumpassword/helpers/MY_access.php | 1 + .../helpers/albumpassword_installer.php | 4 ++-- .../controllers/admin_albumpassword.php | 16 ++++++++-------- 3.1/modules/albumpassword/helpers/MY_access.php | 1 + .../helpers/albumpassword_installer.php | 4 ++-- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/3.0/modules/albumpassword/controllers/admin_albumpassword.php b/3.0/modules/albumpassword/controllers/admin_albumpassword.php index db134c66..425f9ed9 100644 --- a/3.0/modules/albumpassword/controllers/admin_albumpassword.php +++ b/3.0/modules/albumpassword/controllers/admin_albumpassword.php @@ -39,9 +39,9 @@ class Admin_Albumpassword_Controller extends Admin_Controller { // Should protected items be hidden, or completely in-accessable? $albumpassword_group = $form->group("album_password_group"); - $albumpassword_group->checkbox("hideonly") - ->label("Only hide protected albums?") - ->checked(module::get_var("albumpassword", "hideonly")); + $albumpassword_group->checkbox("hideonly") + ->label("Only hide protected albums?") + ->checked(module::get_var("albumpassword", "hideonly")); // Add a save button to the form. $albumpassword_group->submit("save_settings")->value(t("Save")); @@ -56,14 +56,14 @@ class Admin_Albumpassword_Controller extends Admin_Controller { // Prevent Cross Site Request Forgery access::verify_csrf(); - // Retrieve submitted form data. - if (Input::instance()->post("hideonly") == false) { + // Retrieve submitted form data. + if (Input::instance()->post("hideonly") == false) { module::set_var("albumpassword", "hideonly", false); - } else { + } else { module::set_var("albumpassword", "hideonly", true); - } + } // Display a success message and redirect back to the TagsMap admin page. message::success(t("Your settings have been saved.")); url::redirect("admin/albumpassword"); } -} \ No newline at end of file +} diff --git a/3.0/modules/albumpassword/helpers/MY_access.php b/3.0/modules/albumpassword/helpers/MY_access.php index 55f85f33..0d524624 100644 --- a/3.0/modules/albumpassword/helpers/MY_access.php +++ b/3.0/modules/albumpassword/helpers/MY_access.php @@ -28,6 +28,7 @@ class access extends access_Core { } else { self::forbidden(); } + // Begin rWatcher modifications. // This section adds an additional condition onto the view permission that throws a 404 // error if the album has a password assigned. diff --git a/3.0/modules/albumpassword/helpers/albumpassword_installer.php b/3.0/modules/albumpassword/helpers/albumpassword_installer.php index 42af0ddf..1fd20d89 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_installer.php @@ -30,7 +30,7 @@ class albumpassword_installer { // Set the default value for this module's behavior. module::set_var("albumpassword", "hideonly", true); - + // Set the module's version number. module::set_version("albumpassword", 2); } @@ -38,7 +38,7 @@ class albumpassword_installer { static function upgrade($version) { // Set the default value for this module's behavior. module::set_var("albumpassword", "hideonly", true); - + // Set the module's version number. module::set_version("albumpassword", 2); } diff --git a/3.1/modules/albumpassword/controllers/admin_albumpassword.php b/3.1/modules/albumpassword/controllers/admin_albumpassword.php index db134c66..425f9ed9 100644 --- a/3.1/modules/albumpassword/controllers/admin_albumpassword.php +++ b/3.1/modules/albumpassword/controllers/admin_albumpassword.php @@ -39,9 +39,9 @@ class Admin_Albumpassword_Controller extends Admin_Controller { // Should protected items be hidden, or completely in-accessable? $albumpassword_group = $form->group("album_password_group"); - $albumpassword_group->checkbox("hideonly") - ->label("Only hide protected albums?") - ->checked(module::get_var("albumpassword", "hideonly")); + $albumpassword_group->checkbox("hideonly") + ->label("Only hide protected albums?") + ->checked(module::get_var("albumpassword", "hideonly")); // Add a save button to the form. $albumpassword_group->submit("save_settings")->value(t("Save")); @@ -56,14 +56,14 @@ class Admin_Albumpassword_Controller extends Admin_Controller { // Prevent Cross Site Request Forgery access::verify_csrf(); - // Retrieve submitted form data. - if (Input::instance()->post("hideonly") == false) { + // Retrieve submitted form data. + if (Input::instance()->post("hideonly") == false) { module::set_var("albumpassword", "hideonly", false); - } else { + } else { module::set_var("albumpassword", "hideonly", true); - } + } // Display a success message and redirect back to the TagsMap admin page. message::success(t("Your settings have been saved.")); url::redirect("admin/albumpassword"); } -} \ No newline at end of file +} diff --git a/3.1/modules/albumpassword/helpers/MY_access.php b/3.1/modules/albumpassword/helpers/MY_access.php index 55f85f33..0d524624 100644 --- a/3.1/modules/albumpassword/helpers/MY_access.php +++ b/3.1/modules/albumpassword/helpers/MY_access.php @@ -28,6 +28,7 @@ class access extends access_Core { } else { self::forbidden(); } + // Begin rWatcher modifications. // This section adds an additional condition onto the view permission that throws a 404 // error if the album has a password assigned. diff --git a/3.1/modules/albumpassword/helpers/albumpassword_installer.php b/3.1/modules/albumpassword/helpers/albumpassword_installer.php index 42af0ddf..1fd20d89 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_installer.php @@ -30,7 +30,7 @@ class albumpassword_installer { // Set the default value for this module's behavior. module::set_var("albumpassword", "hideonly", true); - + // Set the module's version number. module::set_version("albumpassword", 2); } @@ -38,7 +38,7 @@ class albumpassword_installer { static function upgrade($version) { // Set the default value for this module's behavior. module::set_var("albumpassword", "hideonly", true); - + // Set the module's version number. module::set_version("albumpassword", 2); } From 80f2f225139d80cbee04bf6a7fe9d9a72bde3341 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Mon, 8 Nov 2010 13:45:54 +0800 Subject: [PATCH 080/300] Minor fix. --- 3.0/modules/albumpassword/helpers/MY_access.php | 6 +++--- 3.1/modules/albumpassword/helpers/MY_access.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/MY_access.php b/3.0/modules/albumpassword/helpers/MY_access.php index 0d524624..faf6c1c0 100644 --- a/3.0/modules/albumpassword/helpers/MY_access.php +++ b/3.0/modules/albumpassword/helpers/MY_access.php @@ -30,9 +30,9 @@ class access extends access_Core { } // Begin rWatcher modifications. - // This section adds an additional condition onto the view permission that throws a 404 - // error if the album has a password assigned. - } elseif (($perm_name == "view") && (module::get_var("albumpassword", "hideonly") == false)) { + // Throw a 404 error when a user attempts to access a protected item, + // unless the password has been provided, or the user is the item's owner. + } elseif (module::get_var("albumpassword", "hideonly") == false) { $album_item = ""; do { if ($album_item == "") { diff --git a/3.1/modules/albumpassword/helpers/MY_access.php b/3.1/modules/albumpassword/helpers/MY_access.php index 0d524624..faf6c1c0 100644 --- a/3.1/modules/albumpassword/helpers/MY_access.php +++ b/3.1/modules/albumpassword/helpers/MY_access.php @@ -30,9 +30,9 @@ class access extends access_Core { } // Begin rWatcher modifications. - // This section adds an additional condition onto the view permission that throws a 404 - // error if the album has a password assigned. - } elseif (($perm_name == "view") && (module::get_var("albumpassword", "hideonly") == false)) { + // Throw a 404 error when a user attempts to access a protected item, + // unless the password has been provided, or the user is the item's owner. + } elseif (module::get_var("albumpassword", "hideonly") == false) { $album_item = ""; do { if ($album_item == "") { From b7444e5cfc7e34b5fa2c11172918d2357240569e Mon Sep 17 00:00:00 2001 From: rWatcher Date: Sun, 14 Nov 2010 14:42:29 +0800 Subject: [PATCH 081/300] Initial commit of TagsInAlbum module. --- .../tagsinalbum/helpers/tagsinalbum_block.php | 50 +++++++++++++++++++ 3.0/modules/tagsinalbum/module.info | 3 ++ .../views/tagsinalbum_sidebar.html.php | 28 +++++++++++ .../tagsinalbum/helpers/tagsinalbum_block.php | 50 +++++++++++++++++++ 3.1/modules/tagsinalbum/module.info | 3 ++ .../views/tagsinalbum_sidebar.html.php | 28 +++++++++++ 6 files changed, 162 insertions(+) create mode 100644 3.0/modules/tagsinalbum/helpers/tagsinalbum_block.php create mode 100644 3.0/modules/tagsinalbum/module.info create mode 100644 3.0/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php create mode 100644 3.1/modules/tagsinalbum/helpers/tagsinalbum_block.php create mode 100644 3.1/modules/tagsinalbum/module.info create mode 100644 3.1/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php diff --git a/3.0/modules/tagsinalbum/helpers/tagsinalbum_block.php b/3.0/modules/tagsinalbum/helpers/tagsinalbum_block.php new file mode 100644 index 00000000..d923bc15 --- /dev/null +++ b/3.0/modules/tagsinalbum/helpers/tagsinalbum_block.php @@ -0,0 +1,50 @@ + t("Tags In Album")); + } + + static function get($block_id, $theme) { + $block = ""; + + switch ($block_id) { + case "tagsinalbum": + if (($theme->item) && ($theme->item->is_album())) { + $item = $theme->item; + $all_tags = ORM::factory("tag") + ->join("items_tags", "items_tags.tag_id", "tags.id") + ->join("items", "items.id", "items_tags.item_id", "LEFT") + ->where("items.parent_id", "=", $item->id) + ->order_by("tags.id", "ASC") + ->find_all(); + if (count($all_tags) > 0) { + $block = new Block(); + $block->css_id = "g-tags-in-album-block"; + $block->title = t("In this album"); + $block->content = new View("tagsinalbum_sidebar.html"); + $block->content->all_tags = $all_tags; + } + } + break; + } + return $block; + } +} diff --git a/3.0/modules/tagsinalbum/module.info b/3.0/modules/tagsinalbum/module.info new file mode 100644 index 00000000..94b7699b --- /dev/null +++ b/3.0/modules/tagsinalbum/module.info @@ -0,0 +1,3 @@ +name = "Tags In Album" +description = "Creates a sidebar block to display tags used by photos and videos in the current album." +version = 1 diff --git a/3.0/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php b/3.0/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php new file mode 100644 index 00000000..803e46ee --- /dev/null +++ b/3.0/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php @@ -0,0 +1,28 @@ + +id) { + $tag = ORM::factory("tag", $one_tag->id); + $display_tags[] = array(html::clean($tag->name), $tag->url()); + $last_tagid = $one_tag->id; + } + } + + // Sort the array. + asort($display_tags); + + // Print out the list of tags as clickable links. + $not_first = 0; + foreach ($display_tags as $one_tag) { + if ($not_first++ > 0) { + print ", "; + } + print "" . $one_tag[0] . ""; + } +?> diff --git a/3.1/modules/tagsinalbum/helpers/tagsinalbum_block.php b/3.1/modules/tagsinalbum/helpers/tagsinalbum_block.php new file mode 100644 index 00000000..d923bc15 --- /dev/null +++ b/3.1/modules/tagsinalbum/helpers/tagsinalbum_block.php @@ -0,0 +1,50 @@ + t("Tags In Album")); + } + + static function get($block_id, $theme) { + $block = ""; + + switch ($block_id) { + case "tagsinalbum": + if (($theme->item) && ($theme->item->is_album())) { + $item = $theme->item; + $all_tags = ORM::factory("tag") + ->join("items_tags", "items_tags.tag_id", "tags.id") + ->join("items", "items.id", "items_tags.item_id", "LEFT") + ->where("items.parent_id", "=", $item->id) + ->order_by("tags.id", "ASC") + ->find_all(); + if (count($all_tags) > 0) { + $block = new Block(); + $block->css_id = "g-tags-in-album-block"; + $block->title = t("In this album"); + $block->content = new View("tagsinalbum_sidebar.html"); + $block->content->all_tags = $all_tags; + } + } + break; + } + return $block; + } +} diff --git a/3.1/modules/tagsinalbum/module.info b/3.1/modules/tagsinalbum/module.info new file mode 100644 index 00000000..94b7699b --- /dev/null +++ b/3.1/modules/tagsinalbum/module.info @@ -0,0 +1,3 @@ +name = "Tags In Album" +description = "Creates a sidebar block to display tags used by photos and videos in the current album." +version = 1 diff --git a/3.1/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php b/3.1/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php new file mode 100644 index 00000000..803e46ee --- /dev/null +++ b/3.1/modules/tagsinalbum/views/tagsinalbum_sidebar.html.php @@ -0,0 +1,28 @@ + +id) { + $tag = ORM::factory("tag", $one_tag->id); + $display_tags[] = array(html::clean($tag->name), $tag->url()); + $last_tagid = $one_tag->id; + } + } + + // Sort the array. + asort($display_tags); + + // Print out the list of tags as clickable links. + $not_first = 0; + foreach ($display_tags as $one_tag) { + if ($not_first++ > 0) { + print ", "; + } + print "" . $one_tag[0] . ""; + } +?> From 920cf4dd5ce5467d22a759f98bf50f926df0e7f3 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Fri, 26 Nov 2010 22:12:56 +0100 Subject: [PATCH 082/300] Initial commit --- 3.0/modules/author/helpers/author.php | 98 +++++++++++++++++++ 3.0/modules/author/helpers/author_block.php | 48 +++++++++ 3.0/modules/author/helpers/author_event.php | 32 ++++++ .../author/helpers/author_installer.php | 60 ++++++++++++ 3.0/modules/author/models/author_record.php | 21 ++++ 3.0/modules/author/module.info | 3 + .../author/views/author_block.html.php | 6 ++ 7 files changed, 268 insertions(+) create mode 100644 3.0/modules/author/helpers/author.php create mode 100644 3.0/modules/author/helpers/author_block.php create mode 100644 3.0/modules/author/helpers/author_event.php create mode 100644 3.0/modules/author/helpers/author_installer.php create mode 100644 3.0/modules/author/models/author_record.php create mode 100644 3.0/modules/author/module.info create mode 100644 3.0/modules/author/views/author_block.html.php diff --git a/3.0/modules/author/helpers/author.php b/3.0/modules/author/helpers/author.php new file mode 100644 index 00000000..7411739f --- /dev/null +++ b/3.0/modules/author/helpers/author.php @@ -0,0 +1,98 @@ +is_album()) { return false; } + + $mime = $item->mime_type; + if ($mime == 'image/jpeg' || $mime == 'image/png' || $mime == 'image/gif') {} + else { return false; } + + $owner = ORM::factory("user")->where("id", "=", $item->owner_id)->find(); + $user_name = $owner->full_name; + + $exiv = module::get_var('author', 'exiv_path'); + $exivData = array(); + exec("$exiv -p a " . escapeshellarg($item->file_path()), $exivData); + $has = array(); + $mod = array(); + foreach ($exivData as $line) + { + $tokens = preg_split('/\s+/', $line, 4); + $has[ $tokens[0] ] = $tokens[3]; + } + + $candidates = array( + $has['Xmp.dc.creator'], + $has['Iptc.Application2.Byline'], + $has['Exif.Image.Artist'], + $user_name, + 'Unknown'); + + foreach ($candidates as $cand) { + if ($cand != '') { $byline = $cand; break; } + } + + if (!array_key_exists('Exif.Image.Artist', $has)) { $mod['Exif.Image.Artist'] = $byline; } + if (!array_key_exists('Xmp.dc.creator', $has)) { $mod['Xmp.dc.creator'] = $byline; } + if (!array_key_exists('Iptc.Application2.Byline', $has)) { $mod['Iptc.Application2.Byline'] = $byline; } + + # Apply our own image terms URL. + $terms = module::get_var("author", "usage_terms") + if ($terms != '') { + $mod['Xmp.xmpRights.UsageTerms'] = 'http://wiki.sverok.se/wiki/Bildbank-Bilder'; + } + + # ..and credit. + $credit = module::get_var("author", "credit") + if ($credit != '') { + $mod['Iptc.Application2.Credit'] = $credit; + } + + $line = $exiv . ' '; + foreach ($mod as $key => $value) { + $line .= "-M \"set $key " . escapeshellarg($value) . "\" "; + } + + $files = array( + $item->file_path(), + $item->thumb_path(), + $item->resize_path() + ); + + foreach ($files as $file) { + system("$line " . escapeshellarg($file)); + } + + $record = ORM::factory("author_record")->where("item_id", "=", $item->id)->find(); + if (!$record->loaded()) { + $record->item_id = $item->id; + } + $record->author = $byline; + $record->dirty = 0; + $record->save(); + return $byline; + } + +} diff --git a/3.0/modules/author/helpers/author_block.php b/3.0/modules/author/helpers/author_block.php new file mode 100644 index 00000000..9537a9a5 --- /dev/null +++ b/3.0/modules/author/helpers/author_block.php @@ -0,0 +1,48 @@ + t("Author")); + } + + static function get($block_id, $theme) { + $item = $theme->item; + if ($block_id != 'author' || $item->is_album() ) { + return ''; + } + $record = db::build() + ->select("author") + ->from("author_records") + ->where("item_id", "=", $item->id) + ->execute() + ->current(); + + $byline = $record->author; + if ($byline == '') { + $byline = author::fix($item); + } + + $block = new Block(); + $block->content = new View("author_block.html"); + $block->content->author = $byline; + + return $block; + } +} diff --git a/3.0/modules/author/helpers/author_event.php b/3.0/modules/author/helpers/author_event.php new file mode 100644 index 00000000..ab40341a --- /dev/null +++ b/3.0/modules/author/helpers/author_event.php @@ -0,0 +1,32 @@ +delete("author_records") + ->where("item_id", "=", $item->id) + ->execute(); + } +} diff --git a/3.0/modules/author/helpers/author_installer.php b/3.0/modules/author/helpers/author_installer.php new file mode 100644 index 00000000..ac8f80ea --- /dev/null +++ b/3.0/modules/author/helpers/author_installer.php @@ -0,0 +1,60 @@ +query("CREATE TABLE IF NOT EXISTS {author_records} ( + `id` int(9) NOT NULL auto_increment, + `item_id` INTEGER(9) NOT NULL, + `author` TEXT, + `dirty` BOOLEAN default 1, + PRIMARY KEY (`id`), + KEY(`item_id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("author", 1); + module::set_var("author", "usage_terms", ''); + module::set_var("author", "credit", ''); + + return true; + } + + static function activate() { + gallery::set_path_env( + array( + getenv("PATH"), + module::get_var("gallery", "extra_binary_paths") + )); + + $exiv = exec('which exiv2'); + if ($exiv == '') { + # Proper warning + } + else { + module::set_var("author", "exiv_path", $exiv); + } + } + + static function deactivate() { + } + + static function uninstall() { + Database::instance()->query("DROP TABLE IF EXISTS {author_records};"); + } +} diff --git a/3.0/modules/author/models/author_record.php b/3.0/modules/author/models/author_record.php new file mode 100644 index 00000000..80c59d22 --- /dev/null +++ b/3.0/modules/author/models/author_record.php @@ -0,0 +1,21 @@ + + +
                        +: +
                        + From 34474a24ee15e67e2149c753b69cf09e5d943e8a Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Fri, 26 Nov 2010 22:30:44 +0000 Subject: [PATCH 083/300] removed debug file - security reasons --- 3.0/modules/transcode/debug.php | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 3.0/modules/transcode/debug.php diff --git a/3.0/modules/transcode/debug.php b/3.0/modules/transcode/debug.php deleted file mode 100644 index 7e9d31a9..00000000 --- a/3.0/modules/transcode/debug.php +++ /dev/null @@ -1,50 +0,0 @@ -ffmpeg info"; - $ffmpegPath = whereis("ffmpeg"); - echo "path: " . $ffmpegPath . "
                        "; - $version = @shell_exec($ffmpegPath . " -version"); $version = explode("\n", $version); $version = $version[0]; $version = explode(" ", $version); $version = $version[1]; - echo "version: " . $version . "
                        "; - echo "codecs:
                        " . @shell_exec($ffmpegPath . " -codecs 2>&1") . "

                        "; - echo "formats:
                        " . @shell_exec($ffmpegPath . " -formats") . "

                        "; -} - -function whereis($app) { - $op = @shell_exec("whereis " . $app); - if ($op != "") { - $op = explode(" ", $op); - for ($i = 1; $i < count($op); $i++) { - if (file_exists($op[$i]) && !is_dir($op[$i])) - return $op[$i]; - } - } - return false; -} - -/* -stdClass Object -( - [video] => stdClass Object - ( - [codec] => h264, - [height] => 576 - [width] => 640 - ) - - [audio] => stdClass Object - ( - ) - -) - - */ \ No newline at end of file From df26355f00e9e6cae649b232016839a42b93d472 Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Fri, 26 Nov 2010 22:44:32 +0000 Subject: [PATCH 084/300] added amazon s3 module --- .../aws_s3/controllers/admin_aws_s3.php | 124 ++ .../aws_s3/helpers/MY_embedlinks_block.php | 10 + .../aws_s3/helpers/MY_embedlinks_theme.php | 13 + 3.0/modules/aws_s3/helpers/MY_item.php | 23 + 3.0/modules/aws_s3/helpers/aws_s3.php | 173 +++ 3.0/modules/aws_s3/helpers/aws_s3_event.php | 34 + .../aws_s3/helpers/aws_s3_installer.php | 40 + 3.0/modules/aws_s3/helpers/aws_s3_task.php | 78 + 3.0/modules/aws_s3/lib/s3.php | 1365 +++++++++++++++++ 3.0/modules/aws_s3/models/MY_Item_Model.php | 40 + 3.0/modules/aws_s3/module.info | 3 + .../aws_s3/views/admin_aws_s3.html.php | 13 + 12 files changed, 1916 insertions(+) create mode 100644 3.0/modules/aws_s3/controllers/admin_aws_s3.php create mode 100644 3.0/modules/aws_s3/helpers/MY_embedlinks_block.php create mode 100644 3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php create mode 100644 3.0/modules/aws_s3/helpers/MY_item.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_event.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_installer.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_task.php create mode 100644 3.0/modules/aws_s3/lib/s3.php create mode 100644 3.0/modules/aws_s3/models/MY_Item_Model.php create mode 100644 3.0/modules/aws_s3/module.info create mode 100644 3.0/modules/aws_s3/views/admin_aws_s3.html.php diff --git a/3.0/modules/aws_s3/controllers/admin_aws_s3.php b/3.0/modules/aws_s3/controllers/admin_aws_s3.php new file mode 100644 index 00000000..8ce2e7ea --- /dev/null +++ b/3.0/modules/aws_s3/controllers/admin_aws_s3.php @@ -0,0 +1,124 @@ +_get_s3_form(); + + if (request::method() == "post") { + access::verify_csrf(); + + if ($form->validate()) { + module::set_var("aws_s3", "enabled", (isset($_POST['enabled']) ? true : false)); + module::set_var("aws_s3", "access_key", $_POST['access_key']); + module::set_var("aws_s3", "secret_key", $_POST['secret_key']); + module::set_var("aws_s3", "bucket_name", $_POST['bucket_name']); + module::set_var("aws_s3", "g3id", $_POST['g3id']); + + module::set_var("aws_s3", "url_str", $_POST['url_str']); + module::set_var("aws_s3", "sig_exp", $_POST['sig_exp']); + + module::set_var("aws_s3", "use_ssl", (isset($_POST['use_ssl']) ? true : false)); + + if (module::get_var("aws_s3", "enabled") && !module::get_var("aws_s3", "synced", false)) + site_status::warning( + t('Your site has not yet been syncronised with your Amazon S3 bucket. Content will not appear correctly until you perform syncronisation. Fix this now', + array("url" => html::mark_clean(url::site("admin/maintenance/start/aws_s3_task::sync?csrf=__CSRF__"))) + ), "aws_s3_not_synced"); + + + message::success(t("Settings have been saved")); + url::redirect("admin/aws_s3"); + } + else { + message::error(t("There was a problem with the submitted form. Please check your values and try again.")); + } + } + + $v = new Admin_View("admin.html"); + $v->page_title = t("Amazon S3 Configuration"); + $v->content = new View("admin_aws_s3.html"); + $v->content->form = $form; + $v->content->end = ""; + + echo $v; + } + + private function _get_s3_form() { + $form = new Forge("admin/aws_s3", "", "post", array("id" => "g-admin-s3-form")); + + $group = $form->group("aws_s3")->label(t("Amazon S3 Settings")); + + $group ->checkbox("enabled") + ->id("s3-enabled") + ->checked(module::get_var("aws_s3", "enabled")) + ->label("S3 enabled"); + + $group ->input("access_key") + ->id("s3-access-key") + ->label("Access Key ID") + ->value(module::get_var("aws_s3", "access_key")) + ->rules("required") + ->error_messages("required", "This field is required") + ->message('Sign up to Amazon S3'); + + $group ->input("secret_key") + ->id("s3-secret-key") + ->label("Secret Access Key") + ->value(module::get_var("aws_s3", "secret_key")) + ->rules("required") + ->error_messages("required", "This field is required"); + + $group ->input("bucket_name") + ->id("s3-bucket") + ->label("Bucket Name") + ->value(module::get_var("aws_s3", "bucket_name")) + ->rules("required") + ->error_messages("required", "This field is required") + ->message("Note: This module will not create a bucket if it does not already exist. Please ensure you have already created the bucket and the bucket has the correct ACL permissions before continuing."); + + $group ->input("g3id") + ->id("s3-g3id") + ->label("G3 ID") + ->value(module::get_var("aws_s3", "g3id", md5(time()))) + ->rules("required") + ->error_messages("required", "This field is required") + ->message("This field allows for multiple G3 instances running off of a single S3 bucket."); + + $group ->checkbox("use_ssl") + ->id("s3-use-ssl") + ->checked(module::get_var("aws_s3", "use_ssl")) + ->label("Use SSL for S3 transfers"); + + $group = $form->group("cdn_settings")->label(t("CDN Settings")); + + $group ->input("url_str") + ->id("s3-url-str") + ->label("URL String") + ->value(module::get_var("aws_s3", "url_str", "http://{bucket}.s3.amazonaws.com/g3/{guid}/{resource}")) + ->rules("required") + ->message("Configure the URL to access uploaded resources on the CDN. Use the following variables to define and build up the URL:
                        +• {bucket} - Bucket Name
                        +• {guid} - Unique identifier for this gallery installation
                        +• {resource} - The end path to the resource/object"); + + $group ->input("sig_exp") + ->id("sig_exp") + ->label("Private Content Signature Duration") + ->value(module::get_var("aws_s3", "sig_exp", 60)) + ->rules("required") + ->callback("aws_s3::validate_number") + ->error_messages("not_numeric", "The value provided is not numeric. Please enter a number in this field.") + ->message("Set the time in seconds for the generated signature for access to permission-restricted S3 objects

                        +Note: this module does not yet support the creation of signatures to access private objects on S3 via CloudFront CDN."); + + $form ->submit("save") + ->value("Save Settings"); + + + return $form; + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php new file mode 100644 index 00000000..fe251b1c --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php @@ -0,0 +1,10 @@ +item && $theme->item->view_1 == 1) + parent::get($block_id, $theme); + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php new file mode 100644 index 00000000..5313cf79 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php @@ -0,0 +1,13 @@ +item; + if ($item->view_1 == 1) + return parent::photo_bottom($theme); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_item.php b/3.0/modules/aws_s3/helpers/MY_item.php new file mode 100644 index 00000000..f26aa431 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_item.php @@ -0,0 +1,23 @@ +parent(); + if ($parent->id > 1) { + aws_s3::upload_album_cover($parent); + } + } + + static function remove_album_cover($album) { + parent::remove_album_cover($album); + + if ($album->id > 1) { + aws_s3::remove_album_cover($album); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3.php b/3.0/modules/aws_s3/helpers/aws_s3.php new file mode 100644 index 00000000..9589f495 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3.php @@ -0,0 +1,173 @@ + 0) + return $matches[1]; + return false; + } + + static function log($item) { + if (is_string($item) || is_numeric($item)) {} + else + $item = print_r($item, true); + + $fh = fopen(VARPATH . "modules/aws_s3/log/aws_s3-" . date("Y-m-d") . ".log", "a"); + fwrite($fh, date("Y-m-d H:i:s") . ": " . $item . "\n"); + fclose($fh); + } + + static function upload_item($item) { + self::get_s3(); + + $success_fs = S3::putObjectFile(VARPATH . "albums/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + $success_th = S3::putObjectFile(VARPATH . "thumbs/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + $success_rs = S3::putObjectFile(VARPATH . "resizes/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + + $success = $success_fs && $success_th && $success_rs; + aws_s3::log("item upload success: " . $success); + } + + static function move_item($old_item, $new_item) { + self::get_s3(); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $old_item->relative_path())); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $old_item->relative_path())); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $old_item->relative_path())); + } + + static function remove_item($item) { + self::get_s3(); + + $success_fs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $item->relative_path())); + $success_th = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $item->relative_path())); + $success_rs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $item->relative_path())); + + $success = $success_fs && $success_th && $success_rs; + aws_s3::log("s3 delete success: " . $success); + } + + static function upload_album_cover($album) { + self::get_s3(); + + if (file_exists(VARPATH . "resizes/" . $album->relative_path() . "/.album.jpg")) + $success_rs = S3::putObjectFile(VARPATH . "resizes/" . $album->relative_path() . "/.album.jpg", + module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/rs/" . $album->relative_path() . "/.album.jpg", + ($album->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + else + $success_rs = true; + + if (file_exists(VARPATH . "thumbs/" . $album->relative_path() . "/.album.jpg")) + $success_th = S3::putObjectFile(VARPATH . "thumbs/" . $album->relative_path() . "/.album.jpg", + module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/th/" . $album->relative_path() . "/.album.jpg", + ($album->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + else + $success_th = true; + + $success = $success_rs && $success_th; + aws_s3::log("album cover upload success: " . $success); + } + + static function remove_album_cover($album) { + self::get_s3(); + + $success_th = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/th/" . $album->relative_path() . "/.album.jpg"); + $success_rs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/rs/" . $album->relative_path() . "/.album.jpg"); + + $success = $success_rs && $success_th; + aws_s3::log("album cover removal success: " . $success); + } + + static function getAuthenticatedURL($bucket, $uri) { + self::get_s3(); + + return S3::getAuthenticatedURL($bucket, $uri, 60); + } + + static function validate_number($field) { + if (preg_match("/\D/", $field->value)) + $field->add_error("not_numeric", 1); + } + + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3_event.php b/3.0/modules/aws_s3/helpers/aws_s3_event.php new file mode 100644 index 00000000..bb46de80 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3_event.php @@ -0,0 +1,34 @@ +get("settings_menu") + ->append( + Menu::factory("link") + ->id("aws_s3_link") + ->label(t("Amazon S3")) + ->url(url::site("admin/aws_s3")) + ); + } + + static function item_created($item) { + if ($item->is_album()) + return true; + + aws_s3::log("Item created - " . $item->id); + aws_s3::upload_item($item); + } + + static function item_deleted($item) { + aws_s3::log("Item deleted - " . $item->id); + aws_s3::remove_item($item); + } + + static function item_moved($new_item, $old_item) { + aws_s3::log("Item moved - " . $item->id); + aws_s3::move_item($old_item, $new_item); + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3_installer.php b/3.0/modules/aws_s3/helpers/aws_s3_installer.php new file mode 100644 index 00000000..75b401b3 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3_installer.php @@ -0,0 +1,40 @@ +callback("aws_s3_task::sync") + ->name(t("Syncronise with Amazon S3")) + ->description(t("Syncronise your Gallery 3 data/images with your Amazon S3 bucket")) + ->severity(log::SUCCESS)); + } + + static function sync($task) { + require_once(MODPATH . "aws_s3/lib/s3.php"); + $s3 = new S3(module::get_var("aws_s3", "access_key"), module::get_var("aws_s3", "secret_key")); + + $mode = $task->get("mode", "init"); + switch ($mode) { + case "init": { + aws_s3::log("re-sync task started.."); + batch::start(); + $items = ORM::factory("item")->find_all(); + aws_s3::log("items to sync: " . count($items)); + $task->set("total_count", count($items)); + $task->set("completed", 0); + $task->set("mode", "empty"); + $task->status = "Emptying contents of bucket"; + } break; + case "empty": { // 0 - 10% + aws_s3::log("emptying bucket contents (any files that may already exist in the bucket/prefix path)"); + $bucket = module::get_var("aws_s3", "bucket_name"); + + $resource = aws_s3::get_resource_url(""); + $stuff = array_reverse(S3::getBucket($bucket, $resource)); + foreach ($stuff as $uri => $item) { + aws_s3::log("removing: " . $uri); + S3::deleteObject($bucket, $uri); + } + $task->percent_complete = 10; + $task->set("mode", "upload"); + $task->state = "Commencing upload..."; + } break; + case "upload": { // 10 - 100% + $completed = $task->get("completed", 0); + $items = ORM::factory("item")->find_all(1, $completed); + foreach ($items as $item) { + if ($item->id > 1) { + aws_s3::log("uploading item " . $item->id . " (" . ($completed + 1) . "/" . $task->get("total_count") . ")"); + if ($item->is_album()) + aws_s3::upload_album_cover($item); + else + aws_s3::upload_item($item); + } + $completed++; + } + $task->set("completed", $completed); + $task->percent_complete = round(90 * ($completed / $task->get("total_count"))) + 10; + $task->status = $completed . " of " . $task->get("total_count"). " uploaded."; + + if ($completed == $task->get("total_count")) { + $task->set("mode", "finish"); + } + } break; + case "finish": { + aws_s3::log("completing upload task.."); + $task->percent_complete = 100; + $task->state = "success"; + $task->done = true; + $task->status = "Sync task completed successfully"; + batch::stop(); + module::set_var("aws_s3", "synced", true); + site_status::clear("aws_s3_not_synced"); + } break; + } + + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/lib/s3.php b/3.0/modules/aws_s3/lib/s3.php new file mode 100644 index 00000000..ae9aeb83 --- /dev/null +++ b/3.0/modules/aws_s3/lib/s3.php @@ -0,0 +1,1365 @@ +getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + $results = array(); + if (!isset($rest->body->Buckets)) return $results; + + if ($detailed) { + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) + $results['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID + ); + $results['buckets'] = array(); + foreach ($rest->body->Buckets->Bucket as $b) + $results['buckets'][] = array( + 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) + ); + } else + foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; + + return $results; + } + + + /* + * Get contents for a bucket + * + * If maxKeys is null this method will loop through truncated result sets + * + * @param string $bucket Bucket name + * @param string $prefix Prefix + * @param string $marker Marker (last file listed) + * @param string $maxKeys Max keys (maximum number of keys to return) + * @param string $delimiter Delimiter + * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes + * @return array | false + */ + public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); + if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + $response = $rest->getResponse(); + if ($response->error === false && $response->code !== 200) + $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); + if ($response->error !== false) { + trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING); + return false; + } + + $results = array(); + + $nextMarker = null; + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->IsTruncated) && + (string)$response->body->IsTruncated == 'false') return $results; + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + // Loop through truncated results if maxKeys isn't specified + if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') + do { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + $rest->setParameter('marker', $nextMarker); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + + if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break; + + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + } while ($response !== false && (string)$response->body->IsTruncated == 'true'); + + return $results; + } + + + /** + * Put a bucket + * + * @param string $bucket Bucket name + * @param constant $acl ACL flag + * @param string $location Set as "EU" to create buckets hosted in Europe + * @return boolean + */ + public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { + $rest = new S3Request('PUT', $bucket, ''); + $rest->setAmzHeader('x-amz-acl', $acl); + + if ($location !== false) { + $dom = new DOMDocument; + $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); + $locationConstraint = $dom->createElement('LocationConstraint', strtoupper($location)); + $createBucketConfiguration->appendChild($locationConstraint); + $dom->appendChild($createBucketConfiguration); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + } + $rest = $rest->getResponse(); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Delete an empty bucket + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function deleteBucket($bucket) { + $rest = new S3Request('DELETE', $bucket); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Create input info array for putObject() + * + * @param string $file Input file + * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) + * @return array | false + */ + public static function inputFile($file, $md5sum = true) { + if (!file_exists($file) || !is_file($file) || !is_readable($file)) { + trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING); + return false; + } + return array('file' => $file, 'size' => filesize($file), + 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : + base64_encode(md5_file($file, true))) : ''); + } + + + /** + * Create input array info for putObject() with a resource + * + * @param string $resource Input resource to read from + * @param integer $bufferSize Input byte size + * @param string $md5sum MD5 hash to send (optional) + * @return array | false + */ + public static function inputResource(&$resource, $bufferSize, $md5sum = '') { + if (!is_resource($resource) || $bufferSize < 0) { + trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING); + return false; + } + $input = array('size' => $bufferSize, 'md5sum' => $md5sum); + $input['fp'] =& $resource; + return $input; + } + + + /** + * Put an object + * + * @param mixed $input Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param array $requestHeaders Array of request headers or content type as a string + * @return boolean + */ + public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + if ($input === false) return false; + $rest = new S3Request('PUT', $bucket, $uri); + + if (is_string($input)) $input = array( + 'data' => $input, 'size' => strlen($input), + 'md5sum' => base64_encode(md5($input, true)) + ); + + // Data + if (isset($input['fp'])) + $rest->fp =& $input['fp']; + elseif (isset($input['file'])) + $rest->fp = @fopen($input['file'], 'rb'); + elseif (isset($input['data'])) + $rest->data = $input['data']; + + // Content-Length (required) + if (isset($input['size']) && $input['size'] >= 0) + $rest->size = $input['size']; + else { + if (isset($input['file'])) + $rest->size = filesize($input['file']); + elseif (isset($input['data'])) + $rest->size = strlen($input['data']); + } + + // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) + if (is_array($requestHeaders)) + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + elseif (is_string($requestHeaders)) // Support for legacy contentType parameter + $input['type'] = $requestHeaders; + + // Content-Type + if (!isset($input['type'])) { + if (isset($requestHeaders['Content-Type'])) + $input['type'] =& $requestHeaders['Content-Type']; + elseif (isset($input['file'])) + $input['type'] = self::__getMimeType($input['file']); + else + $input['type'] = 'application/octet-stream'; + } + + // We need to post with Content-Length and Content-Type, MD5 is optional + if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { + $rest->setHeader('Content-Type', $input['type']); + if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); + + $rest->setAmzHeader('x-amz-acl', $acl); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->getResponse(); + } else + $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Put an object from a file (legacy function) + * + * @param string $file Input file path + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { + return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Put an object from a string (legacy function) + * + * @param string $string Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { + return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Get an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param mixed $saveTo Filename or resource to write to + * @return mixed + */ + public static function getObject($bucket, $uri, $saveTo = false) { + $rest = new S3Request('GET', $bucket, $uri); + if ($saveTo !== false) { + if (is_resource($saveTo)) + $rest->fp =& $saveTo; + else + if (($rest->fp = @fopen($saveTo, 'wb')) !== false) + $rest->file = realpath($saveTo); + else + $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); + } + if ($rest->response->error === false) $rest->getResponse(); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", + $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return $rest->response; + } + + + /** + * Get object information + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param boolean $returnInfo Return response information + * @return mixed | false + */ + public static function getObjectInfo($bucket, $uri, $returnInfo = true) { + $rest = new S3Request('HEAD', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; + } + + + /** + * Copy an object + * + * @param string $bucket Source bucket name + * @param string $uri Source object URI + * @param string $bucket Destination bucket name + * @param string $uri Destination object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Optional array of x-amz-meta-* headers + * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) + * @return mixed | false + */ + public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setHeader('Content-Length', 0); + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->setAmzHeader('x-amz-acl', $acl); + $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, $srcUri)); + if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) + $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return isset($rest->body->LastModified, $rest->body->ETag) ? array( + 'time' => strtotime((string)$rest->body->LastModified), + 'hash' => substr((string)$rest->body->ETag, 1, -1) + ) : false; + } + + + /** + * Set logging for a bucket + * + * @param string $bucket Bucket name + * @param string $targetBucket Target bucket (where logs are stored) + * @param string $targetPrefix Log prefix (e,g; domain.com-) + * @return boolean + */ + public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) { + // The S3 log delivery group has to be added to the target bucket's ACP + if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) { + // Only add permissions to the target bucket when they do not exist + $aclWriteSet = false; + $aclReadSet = false; + foreach ($acp['acl'] as $acl) + if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') { + if ($acl['permission'] == 'WRITE') $aclWriteSet = true; + elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; + } + if (!$aclWriteSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' + ); + if (!$aclReadSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' + ); + if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp); + } + + $dom = new DOMDocument; + $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); + $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); + if ($targetBucket !== null) { + if ($targetPrefix == null) $targetPrefix = $bucket . '-'; + $loggingEnabled = $dom->createElement('LoggingEnabled'); + $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); + $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); + // TODO: Add TargetGrants? + $bucketLoggingStatus->appendChild($loggingEnabled); + } + $dom->appendChild($bucketLoggingStatus); + + $rest = new S3Request('PUT', $bucket, ''); + $rest->setParameter('logging', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get logging status for a bucket + * + * This will return false if logging is not enabled. + * Note: To enable logging, you also need to grant write access to the log group + * + * @param string $bucket Bucket name + * @return array | false + */ + public static function getBucketLogging($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('logging', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + if (!isset($rest->body->LoggingEnabled)) return false; // No logging + return array( + 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, + 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, + ); + } + + + /** + * Disable bucket logging + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function disableBucketLogging($bucket) { + return self::setBucketLogging($bucket, null); + } + + + /** + * Get a bucket's location + * + * @param string $bucket Bucket name + * @return string | false + */ + public static function getBucketLocation($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('location', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; + } + + + /** + * Set object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) + * @return boolean + */ + public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) { + $dom = new DOMDocument; + $dom->formatOutput = true; + $accessControlPolicy = $dom->createElement('AccessControlPolicy'); + $accessControlList = $dom->createElement('AccessControlList'); + + // It seems the owner has to be passed along too + $owner = $dom->createElement('Owner'); + $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); + $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); + $accessControlPolicy->appendChild($owner); + + foreach ($acp['acl'] as $g) { + $grant = $dom->createElement('Grant'); + $grantee = $dom->createElement('Grantee'); + $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted) + $grantee->setAttribute('xsi:type', 'CanonicalUser'); + $grantee->appendChild($dom->createElement('ID', $g['id'])); + } elseif (isset($g['email'])) { // AmazonCustomerByEmail + $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); + $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); + } elseif ($g['type'] == 'Group') { // Group + $grantee->setAttribute('xsi:type', 'Group'); + $grantee->appendChild($dom->createElement('URI', $g['uri'])); + } + $grant->appendChild($grantee); + $grant->appendChild($dom->createElement('Permission', $g['permission'])); + $accessControlList->appendChild($grant); + } + + $accessControlPolicy->appendChild($accessControlList); + $dom->appendChild($accessControlPolicy); + + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setParameter('acl', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed | false + */ + public static function getAccessControlPolicy($bucket, $uri = '') { + $rest = new S3Request('GET', $bucket, $uri); + $rest->setParameter('acl', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + + $acp = array(); + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) { + $acp['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName + ); + } + if (isset($rest->body->AccessControlList)) { + $acp['acl'] = array(); + foreach ($rest->body->AccessControlList->Grant as $grant) { + foreach ($grant->Grantee as $grantee) { + if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser + $acp['acl'][] = array( + 'type' => 'CanonicalUser', + 'id' => (string)$grantee->ID, + 'name' => (string)$grantee->DisplayName, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail + $acp['acl'][] = array( + 'type' => 'AmazonCustomerByEmail', + 'email' => (string)$grantee->EmailAddress, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->URI)) // Group + $acp['acl'][] = array( + 'type' => 'Group', + 'uri' => (string)$grantee->URI, + 'permission' => (string)$grant->Permission + ); + else continue; + } + } + } + return $acp; + } + + + /** + * Delete an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return boolean + */ + public static function deleteObject($bucket, $uri) { + $rest = new S3Request('DELETE', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteObject(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a query string authenticated URL + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param integer $lifetime Lifetime in seconds + * @param boolean $hostBucket Use the bucket name as the hostname + * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) + * @return string + */ + public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) { + $expires = time() + $lifetime; + $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea) + return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', + $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires, + urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); + } + + /** + * Get upload POST parameters for form uploads + * + * @param string $bucket Bucket name + * @param string $uriPrefix Object URI prefix + * @param constant $acl ACL constant + * @param integer $lifetime Lifetime in seconds + * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) + * @param string $successRedirect Redirect URL or 200 / 201 status code + * @param array $amzHeaders Array of x-amz-meta-* headers + * @param array $headers Array of request headers or content type as a string + * @param boolean $flashVars Includes additional "Filename" variable posted by Flash + * @return object + */ + public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) { + // Create policy object + $policy = new stdClass; + $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime)); + $policy->conditions = array(); + $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); + $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); + + $obj = new stdClass; // 200 for non-redirect uploads + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $obj->success_action_status = (string)$successRedirect; + else // URL + $obj->success_action_redirect = $successRedirect; + array_push($policy->conditions, $obj); + + array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); + if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); + foreach (array_keys($headers) as $headerKey) + array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); + foreach ($amzHeaders as $headerKey => $headerVal) { + $obj = new stdClass; $obj->{$headerKey} = (string)$headerVal; array_push($policy->conditions, $obj); + } + array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); + $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); + + // Create parameters + $params = new stdClass; + $params->AWSAccessKeyId = self::$__accessKey; + $params->key = $uriPrefix.'${filename}'; + $params->acl = $acl; + $params->policy = $policy; unset($policy); + $params->signature = self::__getHash($params->policy); + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $params->success_action_status = (string)$successRedirect; + else + $params->success_action_redirect = $successRedirect; + foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + return $params; + } + + /** + * Create a CloudFront distribution + * + * @param string $bucket Bucket name + * @param boolean $enabled Enabled (true/false) + * @param array $cnames Array containing CNAME aliases + * @param string $comment Use the bucket name as the hostname + * @return array | false + */ + public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = '') { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('POST', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($bucket.'.s3.amazonaws.com', $enabled, $comment, (string)microtime(true), $cnames); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 201) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) + return self::__parseCloudFrontDistributionConfig($rest->body); + return false; + } + + + /** + * Get CloudFront distribution info + * + * @param string $distributionId Distribution ID from listDistributions() + * @return array | false + */ + public static function getDistribution($distributionId) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getDistribution($distributionId): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Update a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return array | false + */ + public static function updateDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('PUT', '', '2008-06-30/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames']); + $rest->size = strlen($rest->data); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::updateDistribution({$dist['id']}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } else { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Delete a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return boolean + */ + public static function deleteDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a list of CloudFront distributions + * + * @return array + */ + public static function listDistributions() { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listDistributions(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { + $list = array(); + if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { + //$info['marker'] = (string)$rest->body->Marker; + //$info['maxItems'] = (int)$rest->body->MaxItems; + //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; + } + foreach ($rest->body->DistributionSummary as $summary) { + $list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary); + } + return $list; + } + return array(); + } + + + /** + * Get a DistributionConfig DOMDocument + * + * @internal Used to create XML in createDistribution() and updateDistribution() + * @param string $bucket Origin bucket + * @param boolean $enabled Enabled (true/false) + * @param string $comment Comment to append + * @param string $callerReference Caller reference + * @param array $cnames Array of CNAME aliases + * @return string + */ + private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array()) { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $distributionConfig = $dom->createElement('DistributionConfig'); + $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2008-06-30/'); + $distributionConfig->appendChild($dom->createElement('Origin', $bucket)); + $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); + foreach ($cnames as $cname) + $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); + if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); + $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); + $dom->appendChild($distributionConfig); + return $dom->saveXML(); + } + + + /** + * Parse a CloudFront distribution config + * + * @internal Used to parse the CloudFront DistributionConfig node to an array + * @param object &$node DOMNode + * @return array + */ + private static function __parseCloudFrontDistributionConfig(&$node) { + $dist = array(); + if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) { + $dist['id'] = (string)$node->Id; + $dist['status'] = (string)$node->Status; + $dist['time'] = strtotime((string)$node->LastModifiedTime); + $dist['domain'] = (string)$node->DomainName; + } + if (isset($node->CallerReference)) + $dist['callerReference'] = (string)$node->CallerReference; + if (isset($node->Comment)) + $dist['comment'] = (string)$node->Comment; + if (isset($node->Enabled, $node->Origin)) { + $dist['origin'] = (string)$node->Origin; + $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; + } elseif (isset($node->DistributionConfig)) { + $dist = array_merge($dist, self::__parseCloudFrontDistributionConfig($node->DistributionConfig)); + } + if (isset($node->CNAME)) { + $dist['cnames'] = array(); + foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname; + } + return $dist; + } + + + /** + * Grab CloudFront response + * + * @internal Used to parse the CloudFront S3Request::getResponse() output + * @param object &$rest S3Request instance + * @return object + */ + private static function __getCloudFrontResponse(&$rest) { + $rest->getResponse(); + if ($rest->response->error === false && isset($rest->response->body) && + is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); + // Grab CloudFront errors + if (isset($rest->response->body->Error, $rest->response->body->Error->Code, + $rest->response->body->Error->Message)) { + $rest->response->error = array( + 'code' => (string)$rest->response->body->Error->Code, + 'message' => (string)$rest->response->body->Error->Message + ); + unset($rest->response->body); + } + } + return $rest->response; + } + + + /** + * Get MIME type for file + * + * @internal Used to get mime types + * @param string &$file File path + * @return string + */ + public static function __getMimeType(&$file) { + $type = false; + // Fileinfo documentation says fileinfo_open() will use the + // MAGIC env var for the magic file + if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && + ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { + if (($type = finfo_file($finfo, $file)) !== false) { + // Remove the charset and grab the last content-type + $type = explode(' ', str_replace('; charset=', ';charset=', $type)); + $type = array_pop($type); + $type = explode(';', $type); + $type = trim(array_shift($type)); + } + finfo_close($finfo); + + // If anyone is still using mime_content_type() + } elseif (function_exists('mime_content_type')) + $type = trim(mime_content_type($file)); + + if ($type !== false && strlen($type) > 0) return $type; + + // Otherwise do it the old fashioned way + static $exts = array( + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' + ); + $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); + return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; + } + + + /** + * Generate the auth string: "AWS AccessKey:Signature" + * + * @internal Used by S3Request::getResponse() + * @param string $string String to sign + * @return string + */ + public static function __getSignature($string) { + return 'AWS '.self::$__accessKey.':'.self::__getHash($string); + } + + + /** + * Creates a HMAC-SHA1 hash + * + * This uses the hash extension if loaded + * + * @internal Used by __getSignature() + * @param string $string String to sign + * @return string + */ + private static function __getHash($string) { + return base64_encode(extension_loaded('hash') ? + hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1( + (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . + pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^ + (str_repeat(chr(0x36), 64))) . $string))))); + } + +} + +final class S3Request { + private $verb, $bucket, $uri, $resource = '', $parameters = array(), + $amzHeaders = array(), $headers = array( + 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' + ); + public $fp = false, $size = 0, $data = false, $response; + + + /** + * Constructor + * + * @param string $verb Verb + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed + */ + function __construct($verb, $bucket = '', $uri = '', $defaultHost = 's3.amazonaws.com') { + $this->verb = $verb; + $this->bucket = strtolower($bucket); + $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; + + if ($this->bucket !== '') { + $this->headers['Host'] = $this->bucket.'.'.$defaultHost; + $this->resource = '/'.$this->bucket.$this->uri; + } else { + $this->headers['Host'] = $defaultHost; + //$this->resource = strlen($this->uri) > 1 ? '/'.$this->bucket.$this->uri : $this->uri; + $this->resource = $this->uri; + } + $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); + + $this->response = new STDClass; + $this->response->error = false; + } + + + /** + * Set request parameter + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setParameter($key, $value) { + $this->parameters[$key] = $value; + } + + + /** + * Set request header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setHeader($key, $value) { + $this->headers[$key] = $value; + } + + + /** + * Set x-amz-meta-* header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setAmzHeader($key, $value) { + $this->amzHeaders[$key] = $value; + } + + + /** + * Get the S3 response + * + * @return object | false + */ + public function getResponse() { + $query = ''; + if (sizeof($this->parameters) > 0) { + $query = substr($this->uri, -1) !== '?' ? '?' : '&'; + foreach ($this->parameters as $var => $value) + if ($value == null || $value == '') $query .= $var.'&'; + // Parameters should be encoded (thanks Sean O'Dea) + else $query .= $var.'='.rawurlencode($value).'&'; + $query = substr($query, 0, -1); + $this->uri .= $query; + + if (array_key_exists('acl', $this->parameters) || + array_key_exists('location', $this->parameters) || + array_key_exists('torrent', $this->parameters) || + array_key_exists('logging', $this->parameters)) + $this->resource .= $query; + } + $url = ((S3::$useSSL && extension_loaded('openssl')) ? + 'https://':'http://').$this->headers['Host'].$this->uri; + //var_dump($this->bucket, $this->uri, $this->resource, $url); + + // Basic setup + $curl = curl_init(); + curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); + + if (S3::$useSSL) { + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); + } + + curl_setopt($curl, CURLOPT_URL, $url); + + // Headers + $headers = array(); $amz = array(); + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + foreach ($this->headers as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + + // Collect AMZ headers for signature + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; + + // AMZ headers must be sorted + if (sizeof($amz) > 0) { + sort($amz); + $amz = "\n".implode("\n", $amz); + } else $amz = ''; + + // Authorization string (CloudFront stringToSign should only contain a date) + $headers[] = 'Authorization: ' . S3::__getSignature( + $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : + $this->verb."\n".$this->headers['Content-MD5']."\n". + $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource + ); + + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + // Request types + switch ($this->verb) { + case 'GET': break; + case 'PUT': case 'POST': // POST only used for CloudFront + if ($this->fp !== false) { + curl_setopt($curl, CURLOPT_PUT, true); + curl_setopt($curl, CURLOPT_INFILE, $this->fp); + if ($this->size >= 0) + curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); + } elseif ($this->data !== false) { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); + if ($this->size >= 0) + curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size); + } else + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + break; + case 'HEAD': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); + curl_setopt($curl, CURLOPT_NOBODY, true); + break; + case 'DELETE': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + default: break; + } + + // Execute, grab errors + if (curl_exec($curl)) + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + else + $this->response->error = array( + 'code' => curl_errno($curl), + 'message' => curl_error($curl), + 'resource' => $this->resource + ); + + @curl_close($curl); + + // Parse body into XML + if ($this->response->error === false && isset($this->response->headers['type']) && + $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { + $this->response->body = simplexml_load_string($this->response->body); + + // Grab S3 errors + if (!in_array($this->response->code, array(200, 204)) && + isset($this->response->body->Code, $this->response->body->Message)) { + $this->response->error = array( + 'code' => (string)$this->response->body->Code, + 'message' => (string)$this->response->body->Message + ); + if (isset($this->response->body->Resource)) + $this->response->error['resource'] = (string)$this->response->body->Resource; + unset($this->response->body); + } + } + + // Clean up file resources + if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); + + return $this->response; + } + + + /** + * CURL write callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseWriteCallback(&$curl, &$data) { + if ($this->response->code == 200 && $this->fp !== false) + return fwrite($this->fp, $data); + else + $this->response->body .= $data; + return strlen($data); + } + + + /** + * CURL header callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseHeaderCallback(&$curl, &$data) { + if (($strlen = strlen($data)) <= 2) return $strlen; + if (substr($data, 0, 4) == 'HTTP') + $this->response->code = (int)substr($data, 9, 3); + else { + list($header, $value) = explode(': ', trim($data), 2); + if ($header == 'Last-Modified') + $this->response->headers['time'] = strtotime($value); + elseif ($header == 'Content-Length') + $this->response->headers['size'] = (int)$value; + elseif ($header == 'Content-Type') + $this->response->headers['type'] = $value; + elseif ($header == 'ETag') + $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; + elseif (preg_match('/^x-amz-meta-.*$/', $header)) + $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; + } + return $strlen; + } + +} diff --git a/3.0/modules/aws_s3/models/MY_Item_Model.php b/3.0/modules/aws_s3/models/MY_Item_Model.php new file mode 100644 index 00000000..4cacc2fc --- /dev/null +++ b/3.0/modules/aws_s3/models/MY_Item_Model.php @@ -0,0 +1,40 @@ +is_photo()) { + return aws_s3::generate_url("th/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + else if ($this->is_album() && $this->id > 1) { + return aws_s3::generate_url("th/" . $this->relative_path() . "/.album.jpg", ($this->view_1 == 1 ? false : true), $this->updated); + } + else if ($this->is_movie()) { + $relative_path = preg_replace("/...$/", "jpg", $this->relative_path()); + return aws_s3::generate_url("th/" . $relative_path, ($this->view_1 == 1 ? false : true), $this->updated); + } + } + + public function file_url($full_uri=false) { + if (!module::get_var("aws_s3", "enabled")) + return parent::file_url($full_uri); + + return aws_s3::generate_url("fs/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + + public function resize_url($full_uri=false) { + if (!module::get_var("aws_s3", "enabled")) + return parent::resize_url($full_uri); + + if ($this->is_album() && $this->id > 1) { + return aws_s3::generate_url("rs/" . $this->relative_path() . "/.album.jpg", ($this->view_1 == 1 ? false : true), $this->updated); + } + else { + return aws_s3::generate_url("rs/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/module.info b/3.0/modules/aws_s3/module.info new file mode 100644 index 00000000..6c301507 --- /dev/null +++ b/3.0/modules/aws_s3/module.info @@ -0,0 +1,3 @@ +name = "Amazon S3" +description = "Seamlessly transfer your Gallery data to Amazon S3 CDN for a lightning fast gallery" +version = 1 diff --git a/3.0/modules/aws_s3/views/admin_aws_s3.html.php b/3.0/modules/aws_s3/views/admin_aws_s3.html.php new file mode 100644 index 00000000..961186e3 --- /dev/null +++ b/3.0/modules/aws_s3/views/admin_aws_s3.html.php @@ -0,0 +1,13 @@ + + +
                        +

                        + +

                        + +
                        + +
                        +
                        + + From 58820099a3f2c51077290097096bcba4e7f3446f Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sat, 27 Nov 2010 15:30:48 +0100 Subject: [PATCH 085/300] Just a test for cancel button. Need to be polished. --- 3.0/themes/sobriety/css/screen.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/3.0/themes/sobriety/css/screen.css b/3.0/themes/sobriety/css/screen.css index c6c094bb..3fb13ede 100644 --- a/3.0/themes/sobriety/css/screen.css +++ b/3.0/themes/sobriety/css/screen.css @@ -799,6 +799,18 @@ div#g-action-status { height: 8em; } +#g-dialog input[type=submit].submit { + float: right; +} + +#g-dialog a.g-cancel { + float: right; + -moz-appearance: button; + -webkit-appearance: push-button; + /*clear: left;*/ +} + + #g-add-photos-canvas-sd { height: 33px; /*margin-right: 63px;*/ From fcd9802e0c40144b04863ce659780d417fdf07d6 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 00:25:26 +0100 Subject: [PATCH 086/300] Initial commit. There is probably many bugs, but it seems to work... --- 3.0/modules/user_chroot/helpers/MY_access.php | 61 ++++++++++ 3.0/modules/user_chroot/helpers/MY_item.php | 34 ++++++ 3.0/modules/user_chroot/helpers/MY_url.php | 47 ++++++++ .../user_chroot/helpers/user_chroot.php | 41 +++++++ .../user_chroot/helpers/user_chroot_event.php | 112 ++++++++++++++++++ .../helpers/user_chroot_installer.php | 40 +++++++ .../user_chroot/libraries/MY_ORM_MPTT.php | 61 ++++++++++ .../user_chroot/models/user_chroot.php | 22 ++++ 3.0/modules/user_chroot/module.info | 3 + 9 files changed, 421 insertions(+) create mode 100644 3.0/modules/user_chroot/helpers/MY_access.php create mode 100644 3.0/modules/user_chroot/helpers/MY_item.php create mode 100644 3.0/modules/user_chroot/helpers/MY_url.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot_event.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot_installer.php create mode 100644 3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php create mode 100644 3.0/modules/user_chroot/models/user_chroot.php create mode 100644 3.0/modules/user_chroot/module.info diff --git a/3.0/modules/user_chroot/helpers/MY_access.php b/3.0/modules/user_chroot/helpers/MY_access.php new file mode 100644 index 00000000..5f73b4ca --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_access.php @@ -0,0 +1,61 @@ +id == identity::active_user()->id && user_chroot::album() ) { + if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { + return false; + } + } + + return parent::user_can($user, $perm_name, $item); + } +} diff --git a/3.0/modules/user_chroot/helpers/MY_item.php b/3.0/modules/user_chroot/helpers/MY_item.php new file mode 100644 index 00000000..423b8ddc --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_item.php @@ -0,0 +1,34 @@ +and_open() + ->and_where("items.left_ptr", ">=", user_chroot::album()->left_ptr) + ->and_where("items.right_ptr", "<=", user_chroot::album()->right_ptr) + ->close(); + } + + return $model; + } +} diff --git a/3.0/modules/user_chroot/helpers/MY_url.php b/3.0/modules/user_chroot/helpers/MY_url.php new file mode 100644 index 00000000..31818fa4 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_url.php @@ -0,0 +1,47 @@ +relative_url().'/'.Router::$current_uri, '/'); + } + + return parent::parse_url(); + } + + static function site($uri = '', $protocol = FALSE) { + if( user_chroot::album() ) { + $uri = preg_replace('#^'.user_chroot::album()->relative_url().'#', '', $uri); + } + + return parent::site($uri, $protocol); + } +} \ No newline at end of file diff --git a/3.0/modules/user_chroot/helpers/user_chroot.php b/3.0/modules/user_chroot/helpers/user_chroot.php new file mode 100644 index 00000000..4d0a8499 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot.php @@ -0,0 +1,41 @@ +id); + if( $user_chroot->loaded() && $user_chroot->album_id != 0 ) { + $item = ORM::factory("item", $user_chroot->album_id); + if( $item->loaded() ) { + self::$_album = $item; + } + } + } + + return self::$_album; + } +} \ No newline at end of file diff --git a/3.0/modules/user_chroot/helpers/user_chroot_event.php b/3.0/modules/user_chroot/helpers/user_chroot_event.php new file mode 100644 index 00000000..c43c6dc4 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot_event.php @@ -0,0 +1,112 @@ +delete($user->id); + } + + /** + * Called when admin is adding a user + */ + static function user_add_form_admin($user, $form) { + $form->add_user->dropdown("user_chroot") + ->label(t("Root Album")) + ->options(self::createGalleryArray()) + ->selected(0); + } + + /** + * Called after a user has been added + */ + static function user_add_form_admin_completed($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->add_user->user_chroot->value; + $user_chroot->save(); + } + + /** + * Called when admin is editing a user + */ + static function user_edit_form_admin($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + if ($user_chroot->loaded()) { + $selected = $user_chroot->album_id; + } else { + $selected = 0; + } + $form->edit_user->dropdown("user_chroot") + ->label(t("Root Album")) + ->options(self::createGalleryArray()) + ->selected($selected); + } + + /** + * Called after a user had been edited by the admin + */ + static function user_edit_form_admin_completed($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + if ($user_chroot->loaded()) { + $user_chroot->album_id = $form->edit_user->user_chroot->value; + } else { + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->edit_user->user_chroot->value; + } + $user_chroot->save(); + } + + + /** + * Creates an array of galleries + */ + static function createGalleryArray() { + $array[0] = "none"; + $root = ORM::factory("item", 1); + self::tree($root, "", $array); + return $array; + } + + /** + * recursive function to build array for drop down list + */ + static function tree($parent, $dashes, &$array) { + if ($parent->id == "1") { + $array[$parent->id] = ORM::factory("item", 1)->title; + } else { + $array[$parent->id] = "$dashes $parent->name"; + } + + $albums = ORM::factory("item") + ->where("parent_id", "=", $parent->id) + ->where("type", "=", "album") + ->order_by("title", "ASC") + ->find_all(); + foreach ($albums as $album) { + self::tree($album, "-$dashes", $array); + } + return; + } +} diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php new file mode 100644 index 00000000..ee89fb13 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -0,0 +1,40 @@ +query("CREATE TABLE IF NOT EXISTS {user_chroots} ( + `id` int(9) NOT NULL, + `album_id` int(9) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("user_chroot", 1); + } + + /** + * Drops the table of user chroot when the module is uninstalled. + */ + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {user_chroots};"); + } +} diff --git a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php new file mode 100644 index 00000000..e31124ca --- /dev/null +++ b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php @@ -0,0 +1,61 @@ +model_name = inflector::singular($this->table_name); + } + + /** + * Return the parent of this node + * + * @return ORM + */ + /*function parent() { + if( user_chroot::album() && user_chroot::album()->id == $this->id ) { + return null; + } else { + return parent::parent(); + } + }*/ + + /** + * Return all the parents of this node, in order from root to this node's immediate parent. + * + * @return array ORM + */ + function parents() { + $select = $this + ->where("left_ptr", "<=", $this->left_ptr) + ->where("right_ptr", ">=", $this->right_ptr) + ->where("id", "<>", $this->id) + ->order_by("left_ptr", "ASC"); + + if( user_chroot::album() ) { + $select->where("left_ptr", ">=", user_chroot::album()->left_ptr); + $select->where("right_ptr", "<=", user_chroot::album()->right_ptr); + } + + return $select->find_all(); + } +} diff --git a/3.0/modules/user_chroot/models/user_chroot.php b/3.0/modules/user_chroot/models/user_chroot.php new file mode 100644 index 00000000..30184597 --- /dev/null +++ b/3.0/modules/user_chroot/models/user_chroot.php @@ -0,0 +1,22 @@ + Date: Sat, 27 Nov 2010 06:30:44 +0800 Subject: [PATCH 087/300] removed debug file - security reasons --- 3.0/modules/transcode/debug.php | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 3.0/modules/transcode/debug.php diff --git a/3.0/modules/transcode/debug.php b/3.0/modules/transcode/debug.php deleted file mode 100644 index 7e9d31a9..00000000 --- a/3.0/modules/transcode/debug.php +++ /dev/null @@ -1,50 +0,0 @@ -ffmpeg info"; - $ffmpegPath = whereis("ffmpeg"); - echo "path: " . $ffmpegPath . "
                        "; - $version = @shell_exec($ffmpegPath . " -version"); $version = explode("\n", $version); $version = $version[0]; $version = explode(" ", $version); $version = $version[1]; - echo "version: " . $version . "
                        "; - echo "codecs:
                        " . @shell_exec($ffmpegPath . " -codecs 2>&1") . "

                        "; - echo "formats:
                        " . @shell_exec($ffmpegPath . " -formats") . "

                        "; -} - -function whereis($app) { - $op = @shell_exec("whereis " . $app); - if ($op != "") { - $op = explode(" ", $op); - for ($i = 1; $i < count($op); $i++) { - if (file_exists($op[$i]) && !is_dir($op[$i])) - return $op[$i]; - } - } - return false; -} - -/* -stdClass Object -( - [video] => stdClass Object - ( - [codec] => h264, - [height] => 576 - [width] => 640 - ) - - [audio] => stdClass Object - ( - ) - -) - - */ \ No newline at end of file From f25f70ef77b0e4defcde6578d482ef4e2904777f Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Fri, 26 Nov 2010 22:12:56 +0100 Subject: [PATCH 088/300] Initial commit --- 3.0/modules/author/helpers/author.php | 98 +++++++++++++++++++ 3.0/modules/author/helpers/author_block.php | 48 +++++++++ 3.0/modules/author/helpers/author_event.php | 32 ++++++ .../author/helpers/author_installer.php | 60 ++++++++++++ 3.0/modules/author/models/author_record.php | 21 ++++ 3.0/modules/author/module.info | 3 + .../author/views/author_block.html.php | 6 ++ 7 files changed, 268 insertions(+) create mode 100644 3.0/modules/author/helpers/author.php create mode 100644 3.0/modules/author/helpers/author_block.php create mode 100644 3.0/modules/author/helpers/author_event.php create mode 100644 3.0/modules/author/helpers/author_installer.php create mode 100644 3.0/modules/author/models/author_record.php create mode 100644 3.0/modules/author/module.info create mode 100644 3.0/modules/author/views/author_block.html.php diff --git a/3.0/modules/author/helpers/author.php b/3.0/modules/author/helpers/author.php new file mode 100644 index 00000000..7411739f --- /dev/null +++ b/3.0/modules/author/helpers/author.php @@ -0,0 +1,98 @@ +is_album()) { return false; } + + $mime = $item->mime_type; + if ($mime == 'image/jpeg' || $mime == 'image/png' || $mime == 'image/gif') {} + else { return false; } + + $owner = ORM::factory("user")->where("id", "=", $item->owner_id)->find(); + $user_name = $owner->full_name; + + $exiv = module::get_var('author', 'exiv_path'); + $exivData = array(); + exec("$exiv -p a " . escapeshellarg($item->file_path()), $exivData); + $has = array(); + $mod = array(); + foreach ($exivData as $line) + { + $tokens = preg_split('/\s+/', $line, 4); + $has[ $tokens[0] ] = $tokens[3]; + } + + $candidates = array( + $has['Xmp.dc.creator'], + $has['Iptc.Application2.Byline'], + $has['Exif.Image.Artist'], + $user_name, + 'Unknown'); + + foreach ($candidates as $cand) { + if ($cand != '') { $byline = $cand; break; } + } + + if (!array_key_exists('Exif.Image.Artist', $has)) { $mod['Exif.Image.Artist'] = $byline; } + if (!array_key_exists('Xmp.dc.creator', $has)) { $mod['Xmp.dc.creator'] = $byline; } + if (!array_key_exists('Iptc.Application2.Byline', $has)) { $mod['Iptc.Application2.Byline'] = $byline; } + + # Apply our own image terms URL. + $terms = module::get_var("author", "usage_terms") + if ($terms != '') { + $mod['Xmp.xmpRights.UsageTerms'] = 'http://wiki.sverok.se/wiki/Bildbank-Bilder'; + } + + # ..and credit. + $credit = module::get_var("author", "credit") + if ($credit != '') { + $mod['Iptc.Application2.Credit'] = $credit; + } + + $line = $exiv . ' '; + foreach ($mod as $key => $value) { + $line .= "-M \"set $key " . escapeshellarg($value) . "\" "; + } + + $files = array( + $item->file_path(), + $item->thumb_path(), + $item->resize_path() + ); + + foreach ($files as $file) { + system("$line " . escapeshellarg($file)); + } + + $record = ORM::factory("author_record")->where("item_id", "=", $item->id)->find(); + if (!$record->loaded()) { + $record->item_id = $item->id; + } + $record->author = $byline; + $record->dirty = 0; + $record->save(); + return $byline; + } + +} diff --git a/3.0/modules/author/helpers/author_block.php b/3.0/modules/author/helpers/author_block.php new file mode 100644 index 00000000..9537a9a5 --- /dev/null +++ b/3.0/modules/author/helpers/author_block.php @@ -0,0 +1,48 @@ + t("Author")); + } + + static function get($block_id, $theme) { + $item = $theme->item; + if ($block_id != 'author' || $item->is_album() ) { + return ''; + } + $record = db::build() + ->select("author") + ->from("author_records") + ->where("item_id", "=", $item->id) + ->execute() + ->current(); + + $byline = $record->author; + if ($byline == '') { + $byline = author::fix($item); + } + + $block = new Block(); + $block->content = new View("author_block.html"); + $block->content->author = $byline; + + return $block; + } +} diff --git a/3.0/modules/author/helpers/author_event.php b/3.0/modules/author/helpers/author_event.php new file mode 100644 index 00000000..ab40341a --- /dev/null +++ b/3.0/modules/author/helpers/author_event.php @@ -0,0 +1,32 @@ +delete("author_records") + ->where("item_id", "=", $item->id) + ->execute(); + } +} diff --git a/3.0/modules/author/helpers/author_installer.php b/3.0/modules/author/helpers/author_installer.php new file mode 100644 index 00000000..ac8f80ea --- /dev/null +++ b/3.0/modules/author/helpers/author_installer.php @@ -0,0 +1,60 @@ +query("CREATE TABLE IF NOT EXISTS {author_records} ( + `id` int(9) NOT NULL auto_increment, + `item_id` INTEGER(9) NOT NULL, + `author` TEXT, + `dirty` BOOLEAN default 1, + PRIMARY KEY (`id`), + KEY(`item_id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("author", 1); + module::set_var("author", "usage_terms", ''); + module::set_var("author", "credit", ''); + + return true; + } + + static function activate() { + gallery::set_path_env( + array( + getenv("PATH"), + module::get_var("gallery", "extra_binary_paths") + )); + + $exiv = exec('which exiv2'); + if ($exiv == '') { + # Proper warning + } + else { + module::set_var("author", "exiv_path", $exiv); + } + } + + static function deactivate() { + } + + static function uninstall() { + Database::instance()->query("DROP TABLE IF EXISTS {author_records};"); + } +} diff --git a/3.0/modules/author/models/author_record.php b/3.0/modules/author/models/author_record.php new file mode 100644 index 00000000..80c59d22 --- /dev/null +++ b/3.0/modules/author/models/author_record.php @@ -0,0 +1,21 @@ + + +
                        +: +
                        + From 30700cb8de1293b442b73dae462e3fd2e6b4bf31 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 27 Nov 2010 18:35:11 -0800 Subject: [PATCH 089/300] Oops, add in security. Only show viewable children. --- 3.1/modules/albumtree/views/albumtree_block.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/albumtree/views/albumtree_block.html.php b/3.1/modules/albumtree/views/albumtree_block.html.php index 7cf66799..4a73c333 100644 --- a/3.1/modules/albumtree/views/albumtree_block.html.php +++ b/3.1/modules/albumtree/views/albumtree_block.html.php @@ -10,7 +10,7 @@ -children(null, null, array(array("type", "=", "album"))) as $child): ?> +viewable()->children(null, null, array(array("type", "=", "album"))) as $child): ?> From 9d5bb849c3934d16933192af234799eb27075ba3 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Sun, 28 Nov 2010 04:22:50 +0100 Subject: [PATCH 090/300] Fixes for the initial commit which turned out to have been the wrong file (oops) Support for the Debian Stable version of exiv2, 0.16. --- 3.0/modules/author/helpers/author.php | 45 ++++++++++++++----- .../author/helpers/author_installer.php | 4 ++ 3.0/modules/author/module.info | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/3.0/modules/author/helpers/author.php b/3.0/modules/author/helpers/author.php index 7411739f..84dbb74a 100644 --- a/3.0/modules/author/helpers/author.php +++ b/3.0/modules/author/helpers/author.php @@ -33,8 +33,25 @@ class author_Core { $user_name = $owner->full_name; $exiv = module::get_var('author', 'exiv_path'); + $version = module::get_var('author', 'exiv_version'); + + /* + Debian stable ships with exiv2 0.16 at the time of writing. You get + roughly the same output out of the utility as with 0.20, but you have + to invoke it several times. + + The real threshhold for this might be somewhere between 0.16 and 0.20, + but the 0.16 way of doing things is forward compatible. + */ $exivData = array(); - exec("$exiv -p a " . escapeshellarg($item->file_path()), $exivData); + if ($version < 0.20) { + exec("$exiv -p x " . escapeshellarg($item->file_path()), $exivData); + exec("$exiv -p i " . escapeshellarg($item->file_path()), $exivData); + exec("$exiv -p t " . escapeshellarg($item->file_path()), $exivData); + } else { + exec("$exiv -p a " . escapeshellarg($item->file_path()), $exivData); + } + $has = array(); $mod = array(); foreach ($exivData as $line) @@ -55,20 +72,26 @@ class author_Core { } if (!array_key_exists('Exif.Image.Artist', $has)) { $mod['Exif.Image.Artist'] = $byline; } - if (!array_key_exists('Xmp.dc.creator', $has)) { $mod['Xmp.dc.creator'] = $byline; } if (!array_key_exists('Iptc.Application2.Byline', $has)) { $mod['Iptc.Application2.Byline'] = $byline; } - - # Apply our own image terms URL. - $terms = module::get_var("author", "usage_terms") - if ($terms != '') { - $mod['Xmp.xmpRights.UsageTerms'] = 'http://wiki.sverok.se/wiki/Bildbank-Bilder'; - } - - # ..and credit. - $credit = module::get_var("author", "credit") + + /* Apply the credit block */ + $credit = module::get_var("author", "credit"); if ($credit != '') { $mod['Iptc.Application2.Credit'] = $credit; } + + /* + Older versions doesn't support XMP writing. + */ + if ($version >= 0.20) { + if (!array_key_exists('Xmp.dc.creator', $has)) { $mod['Xmp.dc.creator'] = $byline; } + + /* Apply our own image terms URL */ + $terms = module::get_var("author", "usage_terms"); + if ($terms != '') { + $mod['Xmp.xmpRights.UsageTerms'] = 'http://wiki.sverok.se/wiki/Bildbank-Bilder'; + } + } $line = $exiv . ' '; foreach ($mod as $key => $value) { diff --git a/3.0/modules/author/helpers/author_installer.php b/3.0/modules/author/helpers/author_installer.php index ac8f80ea..9130df9d 100644 --- a/3.0/modules/author/helpers/author_installer.php +++ b/3.0/modules/author/helpers/author_installer.php @@ -48,6 +48,10 @@ class author_installer { } else { module::set_var("author", "exiv_path", $exiv); + $out = array(); + exec("$exiv -V", $out); + $parts = split(' ', $out[0]); + module::set_var("author", "exiv_version", $parts[1]); } } diff --git a/3.0/modules/author/module.info b/3.0/modules/author/module.info index 68cbf6bf..4a9b80b6 100644 --- a/3.0/modules/author/module.info +++ b/3.0/modules/author/module.info @@ -1,3 +1,3 @@ name = "Author" description = "Allows for the display and modification of the Author/Photographer/Byline data in photos." -version = 1 +version = 2 From 356729160943e7ae4730cce1ed808e928a825689 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Sun, 28 Nov 2010 11:22:50 +0800 Subject: [PATCH 091/300] Fixes for the initial commit which turned out to have been the wrong file (oops) Support for the Debian Stable version of exiv2, 0.16. --- 3.0/modules/author/helpers/author.php | 45 ++++++++++++++----- .../author/helpers/author_installer.php | 4 ++ 3.0/modules/author/module.info | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/3.0/modules/author/helpers/author.php b/3.0/modules/author/helpers/author.php index 7411739f..84dbb74a 100644 --- a/3.0/modules/author/helpers/author.php +++ b/3.0/modules/author/helpers/author.php @@ -33,8 +33,25 @@ class author_Core { $user_name = $owner->full_name; $exiv = module::get_var('author', 'exiv_path'); + $version = module::get_var('author', 'exiv_version'); + + /* + Debian stable ships with exiv2 0.16 at the time of writing. You get + roughly the same output out of the utility as with 0.20, but you have + to invoke it several times. + + The real threshhold for this might be somewhere between 0.16 and 0.20, + but the 0.16 way of doing things is forward compatible. + */ $exivData = array(); - exec("$exiv -p a " . escapeshellarg($item->file_path()), $exivData); + if ($version < 0.20) { + exec("$exiv -p x " . escapeshellarg($item->file_path()), $exivData); + exec("$exiv -p i " . escapeshellarg($item->file_path()), $exivData); + exec("$exiv -p t " . escapeshellarg($item->file_path()), $exivData); + } else { + exec("$exiv -p a " . escapeshellarg($item->file_path()), $exivData); + } + $has = array(); $mod = array(); foreach ($exivData as $line) @@ -55,20 +72,26 @@ class author_Core { } if (!array_key_exists('Exif.Image.Artist', $has)) { $mod['Exif.Image.Artist'] = $byline; } - if (!array_key_exists('Xmp.dc.creator', $has)) { $mod['Xmp.dc.creator'] = $byline; } if (!array_key_exists('Iptc.Application2.Byline', $has)) { $mod['Iptc.Application2.Byline'] = $byline; } - - # Apply our own image terms URL. - $terms = module::get_var("author", "usage_terms") - if ($terms != '') { - $mod['Xmp.xmpRights.UsageTerms'] = 'http://wiki.sverok.se/wiki/Bildbank-Bilder'; - } - - # ..and credit. - $credit = module::get_var("author", "credit") + + /* Apply the credit block */ + $credit = module::get_var("author", "credit"); if ($credit != '') { $mod['Iptc.Application2.Credit'] = $credit; } + + /* + Older versions doesn't support XMP writing. + */ + if ($version >= 0.20) { + if (!array_key_exists('Xmp.dc.creator', $has)) { $mod['Xmp.dc.creator'] = $byline; } + + /* Apply our own image terms URL */ + $terms = module::get_var("author", "usage_terms"); + if ($terms != '') { + $mod['Xmp.xmpRights.UsageTerms'] = 'http://wiki.sverok.se/wiki/Bildbank-Bilder'; + } + } $line = $exiv . ' '; foreach ($mod as $key => $value) { diff --git a/3.0/modules/author/helpers/author_installer.php b/3.0/modules/author/helpers/author_installer.php index ac8f80ea..9130df9d 100644 --- a/3.0/modules/author/helpers/author_installer.php +++ b/3.0/modules/author/helpers/author_installer.php @@ -48,6 +48,10 @@ class author_installer { } else { module::set_var("author", "exiv_path", $exiv); + $out = array(); + exec("$exiv -V", $out); + $parts = split(' ', $out[0]); + module::set_var("author", "exiv_version", $parts[1]); } } diff --git a/3.0/modules/author/module.info b/3.0/modules/author/module.info index 68cbf6bf..4a9b80b6 100644 --- a/3.0/modules/author/module.info +++ b/3.0/modules/author/module.info @@ -1,3 +1,3 @@ name = "Author" description = "Allows for the display and modification of the Author/Photographer/Byline data in photos." -version = 1 +version = 2 From db479a61e265db3a8084fbff8d41161295c6d512 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sat, 27 Nov 2010 22:30:48 +0800 Subject: [PATCH 092/300] Just a test for cancel button. Need to be polished. --- 3.0/themes/sobriety/css/screen.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/3.0/themes/sobriety/css/screen.css b/3.0/themes/sobriety/css/screen.css index 7418c954..bf527138 100644 --- a/3.0/themes/sobriety/css/screen.css +++ b/3.0/themes/sobriety/css/screen.css @@ -801,6 +801,18 @@ div#g-action-status { height: 8em; } +#g-dialog input[type=submit].submit { + float: right; +} + +#g-dialog a.g-cancel { + float: right; + -moz-appearance: button; + -webkit-appearance: push-button; + /*clear: left;*/ +} + + #g-add-photos-canvas-sd { height: 33px; /*margin-right: 63px;*/ From d84ea788b25f95e70fd02b3216acc13be8f0ca40 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 07:25:26 +0800 Subject: [PATCH 093/300] Initial commit. There is probably many bugs, but it seems to work... --- 3.0/modules/user_chroot/helpers/MY_access.php | 61 ++++++++++ 3.0/modules/user_chroot/helpers/MY_item.php | 34 ++++++ 3.0/modules/user_chroot/helpers/MY_url.php | 47 ++++++++ .../user_chroot/helpers/user_chroot.php | 41 +++++++ .../user_chroot/helpers/user_chroot_event.php | 112 ++++++++++++++++++ .../helpers/user_chroot_installer.php | 40 +++++++ .../user_chroot/libraries/MY_ORM_MPTT.php | 61 ++++++++++ .../user_chroot/models/user_chroot.php | 22 ++++ 3.0/modules/user_chroot/module.info | 3 + 9 files changed, 421 insertions(+) create mode 100644 3.0/modules/user_chroot/helpers/MY_access.php create mode 100644 3.0/modules/user_chroot/helpers/MY_item.php create mode 100644 3.0/modules/user_chroot/helpers/MY_url.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot_event.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot_installer.php create mode 100644 3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php create mode 100644 3.0/modules/user_chroot/models/user_chroot.php create mode 100644 3.0/modules/user_chroot/module.info diff --git a/3.0/modules/user_chroot/helpers/MY_access.php b/3.0/modules/user_chroot/helpers/MY_access.php new file mode 100644 index 00000000..5f73b4ca --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_access.php @@ -0,0 +1,61 @@ +id == identity::active_user()->id && user_chroot::album() ) { + if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { + return false; + } + } + + return parent::user_can($user, $perm_name, $item); + } +} diff --git a/3.0/modules/user_chroot/helpers/MY_item.php b/3.0/modules/user_chroot/helpers/MY_item.php new file mode 100644 index 00000000..423b8ddc --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_item.php @@ -0,0 +1,34 @@ +and_open() + ->and_where("items.left_ptr", ">=", user_chroot::album()->left_ptr) + ->and_where("items.right_ptr", "<=", user_chroot::album()->right_ptr) + ->close(); + } + + return $model; + } +} diff --git a/3.0/modules/user_chroot/helpers/MY_url.php b/3.0/modules/user_chroot/helpers/MY_url.php new file mode 100644 index 00000000..31818fa4 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_url.php @@ -0,0 +1,47 @@ +relative_url().'/'.Router::$current_uri, '/'); + } + + return parent::parse_url(); + } + + static function site($uri = '', $protocol = FALSE) { + if( user_chroot::album() ) { + $uri = preg_replace('#^'.user_chroot::album()->relative_url().'#', '', $uri); + } + + return parent::site($uri, $protocol); + } +} \ No newline at end of file diff --git a/3.0/modules/user_chroot/helpers/user_chroot.php b/3.0/modules/user_chroot/helpers/user_chroot.php new file mode 100644 index 00000000..4d0a8499 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot.php @@ -0,0 +1,41 @@ +id); + if( $user_chroot->loaded() && $user_chroot->album_id != 0 ) { + $item = ORM::factory("item", $user_chroot->album_id); + if( $item->loaded() ) { + self::$_album = $item; + } + } + } + + return self::$_album; + } +} \ No newline at end of file diff --git a/3.0/modules/user_chroot/helpers/user_chroot_event.php b/3.0/modules/user_chroot/helpers/user_chroot_event.php new file mode 100644 index 00000000..c43c6dc4 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot_event.php @@ -0,0 +1,112 @@ +delete($user->id); + } + + /** + * Called when admin is adding a user + */ + static function user_add_form_admin($user, $form) { + $form->add_user->dropdown("user_chroot") + ->label(t("Root Album")) + ->options(self::createGalleryArray()) + ->selected(0); + } + + /** + * Called after a user has been added + */ + static function user_add_form_admin_completed($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->add_user->user_chroot->value; + $user_chroot->save(); + } + + /** + * Called when admin is editing a user + */ + static function user_edit_form_admin($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + if ($user_chroot->loaded()) { + $selected = $user_chroot->album_id; + } else { + $selected = 0; + } + $form->edit_user->dropdown("user_chroot") + ->label(t("Root Album")) + ->options(self::createGalleryArray()) + ->selected($selected); + } + + /** + * Called after a user had been edited by the admin + */ + static function user_edit_form_admin_completed($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + if ($user_chroot->loaded()) { + $user_chroot->album_id = $form->edit_user->user_chroot->value; + } else { + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->edit_user->user_chroot->value; + } + $user_chroot->save(); + } + + + /** + * Creates an array of galleries + */ + static function createGalleryArray() { + $array[0] = "none"; + $root = ORM::factory("item", 1); + self::tree($root, "", $array); + return $array; + } + + /** + * recursive function to build array for drop down list + */ + static function tree($parent, $dashes, &$array) { + if ($parent->id == "1") { + $array[$parent->id] = ORM::factory("item", 1)->title; + } else { + $array[$parent->id] = "$dashes $parent->name"; + } + + $albums = ORM::factory("item") + ->where("parent_id", "=", $parent->id) + ->where("type", "=", "album") + ->order_by("title", "ASC") + ->find_all(); + foreach ($albums as $album) { + self::tree($album, "-$dashes", $array); + } + return; + } +} diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php new file mode 100644 index 00000000..ee89fb13 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -0,0 +1,40 @@ +query("CREATE TABLE IF NOT EXISTS {user_chroots} ( + `id` int(9) NOT NULL, + `album_id` int(9) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("user_chroot", 1); + } + + /** + * Drops the table of user chroot when the module is uninstalled. + */ + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {user_chroots};"); + } +} diff --git a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php new file mode 100644 index 00000000..e31124ca --- /dev/null +++ b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php @@ -0,0 +1,61 @@ +model_name = inflector::singular($this->table_name); + } + + /** + * Return the parent of this node + * + * @return ORM + */ + /*function parent() { + if( user_chroot::album() && user_chroot::album()->id == $this->id ) { + return null; + } else { + return parent::parent(); + } + }*/ + + /** + * Return all the parents of this node, in order from root to this node's immediate parent. + * + * @return array ORM + */ + function parents() { + $select = $this + ->where("left_ptr", "<=", $this->left_ptr) + ->where("right_ptr", ">=", $this->right_ptr) + ->where("id", "<>", $this->id) + ->order_by("left_ptr", "ASC"); + + if( user_chroot::album() ) { + $select->where("left_ptr", ">=", user_chroot::album()->left_ptr); + $select->where("right_ptr", "<=", user_chroot::album()->right_ptr); + } + + return $select->find_all(); + } +} diff --git a/3.0/modules/user_chroot/models/user_chroot.php b/3.0/modules/user_chroot/models/user_chroot.php new file mode 100644 index 00000000..30184597 --- /dev/null +++ b/3.0/modules/user_chroot/models/user_chroot.php @@ -0,0 +1,22 @@ + Date: Sat, 27 Nov 2010 22:27:22 -0800 Subject: [PATCH 094/300] use sample values --- 3.0/modules/ldap/config/identity.php | 12 ++++++------ 3.1/modules/ldap/config/identity.php | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/3.0/modules/ldap/config/identity.php b/3.0/modules/ldap/config/identity.php index 370d973a..8c4136b2 100644 --- a/3.0/modules/ldap/config/identity.php +++ b/3.0/modules/ldap/config/identity.php @@ -34,13 +34,13 @@ $config["ldap"] = array( "driver" => "ldap", "allow_updates" => false, "params" => array( - "groups" => array("eng", "google", "guest"), + "groups" => array("engineering", "everybody", "guest"), "everybody_group" => "guest", - "registered_users_group" => "google", - "admins" => array("mediratta", "martinm"), - "url" => "ldaps://ldap.corp.google.com/", - "group_domain" => "ou=Posix,ou=Groups,dc=google,dc=com", - "user_domain" => "ou=People,dc=google,dc=com", + "registered_users_group" => "everybody", + "admins" => array("alice", "bob"), + "url" => "ldaps://ldap.mycompany.com/", + "group_domain" => "ou=Posix,ou=Groups,dc=ymcompany,dc=com", + "user_domain" => "ou=People,dc=MyCompany,dc=com", "bind_rdn" => null, "bind_password" => null, ) diff --git a/3.1/modules/ldap/config/identity.php b/3.1/modules/ldap/config/identity.php index 370d973a..8c4136b2 100644 --- a/3.1/modules/ldap/config/identity.php +++ b/3.1/modules/ldap/config/identity.php @@ -34,13 +34,13 @@ $config["ldap"] = array( "driver" => "ldap", "allow_updates" => false, "params" => array( - "groups" => array("eng", "google", "guest"), + "groups" => array("engineering", "everybody", "guest"), "everybody_group" => "guest", - "registered_users_group" => "google", - "admins" => array("mediratta", "martinm"), - "url" => "ldaps://ldap.corp.google.com/", - "group_domain" => "ou=Posix,ou=Groups,dc=google,dc=com", - "user_domain" => "ou=People,dc=google,dc=com", + "registered_users_group" => "everybody", + "admins" => array("alice", "bob"), + "url" => "ldaps://ldap.mycompany.com/", + "group_domain" => "ou=Posix,ou=Groups,dc=ymcompany,dc=com", + "user_domain" => "ou=People,dc=MyCompany,dc=com", "bind_rdn" => null, "bind_password" => null, ) From ad92f87d36aec4c02f527383733686bb731a9ff3 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 12:18:42 +0100 Subject: [PATCH 095/300] - Code cleanup - Fix some bugs - Serious performances improvement when building the tree (regression: tree is not alphabetically ordered) --- .../user_chroot/helpers/user_chroot_event.php | 122 +++++++++--------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_event.php b/3.0/modules/user_chroot/helpers/user_chroot_event.php index c43c6dc4..51027a44 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_event.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_event.php @@ -21,92 +21,94 @@ class user_chroot_event_Core { /** - * Called just before a user is deleted. This will remove the user from - * the user_chroot directory. + * Called just before a user deletion. */ - static function user_before_delete($user) { - ORM::factory("user_chroot")->delete($user->id); + public static function user_before_delete($user) { + ORM::factory('user_chroot', $user->id)->delete(); } /** - * Called when admin is adding a user + * Called just before an item deletion. + */ + public static function item_before_delete($item) { + if( $item->is_album() ) { + ORM::factory('user_chroot')->where('album_id', '=', $item->id)->delete(); + } + } + + /** + * Called when building the 'Add user' form for an admin. */ static function user_add_form_admin($user, $form) { - $form->add_user->dropdown("user_chroot") + $form->add_user->dropdown('user_chroot') ->label(t("Root Album")) - ->options(self::createGalleryArray()) - ->selected(0); + ->options(self::albumsTreeArray()) + ->selected(1); } /** - * Called after a user has been added + * Called just after a user has been added by an admin. */ - static function user_add_form_admin_completed($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - $user_chroot->id = $user->id; - $user_chroot->album_id = $form->add_user->user_chroot->value; - $user_chroot->save(); - } - - /** - * Called when admin is editing a user - */ - static function user_edit_form_admin($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - if ($user_chroot->loaded()) { - $selected = $user_chroot->album_id; - } else { - $selected = 0; + public static function user_add_form_admin_completed($user, $form) { + if( $form->add_user->user_chroot->value > 1 ) { + $user_chroot = ORM::factory('user_chroot'); + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->add_user->user_chroot->value; + $user_chroot->save(); } - $form->edit_user->dropdown("user_chroot") + } + + /** + * Called when building the 'Edit user' form for an admin. + */ + public static function user_edit_form_admin($user, $form) { + $user_chroot = ORM::factory('user_chroot', $user->id); + + $selected = ( $user_chroot->loaded() ) + ? $user_chroot->album_id + : 1; + + $form->edit_user->dropdown('user_chroot') ->label(t("Root Album")) - ->options(self::createGalleryArray()) + ->options(self::albumsTreeArray()) ->selected($selected); } /** - * Called after a user had been edited by the admin + * Called just after a user has been edited by an admin. */ - static function user_edit_form_admin_completed($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - if ($user_chroot->loaded()) { - $user_chroot->album_id = $form->edit_user->user_chroot->value; + public static function user_edit_form_admin_completed($user, $form) { + if( $form->edit_user->user_chroot->value <= 1 ) { + ORM::factory('user_chroot')->delete($user->id); + } else { - $user_chroot->id = $user->id; + $user_chroot = ORM::factory('user_chroot', $user->id); + + if( !$user_chroot->loaded() ) { + $user_chroot = ORM::factory('user_chroot'); + $user_chroot->id = $user->id; + } + $user_chroot->album_id = $form->edit_user->user_chroot->value; + $user_chroot->save(); } - $user_chroot->save(); - } - - - /** - * Creates an array of galleries - */ - static function createGalleryArray() { - $array[0] = "none"; - $root = ORM::factory("item", 1); - self::tree($root, "", $array); - return $array; } /** - * recursive function to build array for drop down list + * Generate an array representing the hierarchy of albums. */ - static function tree($parent, $dashes, &$array) { - if ($parent->id == "1") { - $array[$parent->id] = ORM::factory("item", 1)->title; - } else { - $array[$parent->id] = "$dashes $parent->name"; - } - - $albums = ORM::factory("item") - ->where("parent_id", "=", $parent->id) - ->where("type", "=", "album") - ->order_by("title", "ASC") + private static function albumsTreeArray($level_marker = '    ') { + $tree = array(); + $albums = ORM::factory('item') + ->where('type', '=', 'album') + ->order_by('left_ptr', 'ASC') ->find_all(); - foreach ($albums as $album) { - self::tree($album, "-$dashes", $array); + + foreach($albums as $album) { + $tree[$album->id] = html::clean( + str_repeat($level_marker, $album->level - 1).' '.$album->title ); } - return; + + return $tree; } } From e64d34e9d776db59f0d79929833695b90f5a3630 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 12:22:01 +0100 Subject: [PATCH 096/300] Comments --- 3.0/modules/user_chroot/helpers/user_chroot_installer.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index ee89fb13..f12a2ed1 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -19,6 +19,9 @@ */ class user_chroot_installer { + /** + * Create the table user_chroot when installing the module. + */ static function install() { $db = Database::instance(); $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( @@ -31,7 +34,7 @@ class user_chroot_installer { } /** - * Drops the table of user chroot when the module is uninstalled. + * Drops the table user_chroot when uninstalling the module. */ static function uninstall() { $db = Database::instance(); From f51689d3aad49af65e2955df32e3e665cdff5d22 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 12:23:35 +0100 Subject: [PATCH 097/300] Explicit is better than implicit... --- 3.0/modules/user_chroot/helpers/user_chroot_installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index f12a2ed1..f5f597c4 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -22,7 +22,7 @@ class user_chroot_installer { /** * Create the table user_chroot when installing the module. */ - static function install() { + public static function install() { $db = Database::instance(); $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( `id` int(9) NOT NULL, @@ -36,7 +36,7 @@ class user_chroot_installer { /** * Drops the table user_chroot when uninstalling the module. */ - static function uninstall() { + public static function uninstall() { $db = Database::instance(); $db->query("DROP TABLE IF EXISTS {user_chroots};"); } From 3ff13482ff7638c4e05b0e21cff51400c97052a7 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 12:37:57 +0100 Subject: [PATCH 098/300] - Code cleanup - Comments --- 3.0/modules/user_chroot/helpers/user_chroot.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot.php b/3.0/modules/user_chroot/helpers/user_chroot.php index 4d0a8499..8afa753f 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot.php +++ b/3.0/modules/user_chroot/helpers/user_chroot.php @@ -21,18 +21,21 @@ class user_chroot_Core { private static $_album = null; + /** + * Return the root album of the current user, or false if the user is not + * chrooted. + */ public static function album() { if( is_null(self::$_album) ) { self::$_album = false; - $user = identity::active_user(); + $item = ORM::factory('item') + ->join('user_chroots', 'items.id', 'user_chroots.album_id') + ->where('user_chroots.id', '=', identity::active_user()->id) + ->find(); - $user_chroot = ORM::factory("user_chroot", $user->id); - if( $user_chroot->loaded() && $user_chroot->album_id != 0 ) { - $item = ORM::factory("item", $user_chroot->album_id); - if( $item->loaded() ) { - self::$_album = $item; - } + if( $item->loaded() ) { + self::$_album = $item; } } From acb895d8748e371dfac9959d874c08a58e0c5fd7 Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sun, 28 Nov 2010 11:59:40 +0000 Subject: [PATCH 099/300] added aws_s3 module --- .../aws_s3/helpers/MY_embedlinks_block.php | 20 ++++++++++++++++++- .../aws_s3/helpers/MY_embedlinks_theme.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/MY_item.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3_event.php | 20 ++++++++++++++++++- .../aws_s3/helpers/aws_s3_installer.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3_task.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/models/MY_Item_Model.php | 20 ++++++++++++++++++- 8 files changed, 152 insertions(+), 8 deletions(-) diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php index fe251b1c..e9ca1611 100644 --- a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php @@ -1,4 +1,22 @@ - Date: Sun, 28 Nov 2010 16:03:57 +0100 Subject: [PATCH 100/300] - Overload a forgotten method - Code cleanup --- 3.0/modules/user_chroot/helpers/MY_item.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_item.php b/3.0/modules/user_chroot/helpers/MY_item.php index 423b8ddc..d6368f18 100644 --- a/3.0/modules/user_chroot/helpers/MY_item.php +++ b/3.0/modules/user_chroot/helpers/MY_item.php @@ -24,11 +24,17 @@ class item extends item_Core { if( user_chroot::album() ) { $model->and_open() - ->and_where("items.left_ptr", ">=", user_chroot::album()->left_ptr) - ->and_where("items.right_ptr", "<=", user_chroot::album()->right_ptr) + ->and_where('items.left_ptr', '>=', user_chroot::album()->left_ptr) + ->and_where('items.right_ptr', '<=', user_chroot::album()->right_ptr) ->close(); } return $model; } + + static function root() { + return ( user_chroot::album() ) + ? user_chroot::album() + : parent::root(); + } } From a34f257b5064c961f1425eda11eab837fe6af101 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 16:04:59 +0100 Subject: [PATCH 101/300] - Bugfix - Comments - Code cleanup --- 3.0/modules/user_chroot/helpers/MY_url.php | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_url.php b/3.0/modules/user_chroot/helpers/MY_url.php index 31818fa4..b693c9c5 100644 --- a/3.0/modules/user_chroot/helpers/MY_url.php +++ b/3.0/modules/user_chroot/helpers/MY_url.php @@ -18,25 +18,40 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -// /!\ Hack: There is no good way to extend gallery url +/* + * /!\ Hack + * Module 'gallery' already provides a class 'url' which extends url_Core. This + * hack renames class 'url', so user_chroot can have its own (which extends the + * original) + */ $gallery_url = file_get_contents(MODPATH . 'gallery/helpers/MY_url.php'); -$gallery_url = preg_replace('#^<\?php #', '', $gallery_url); -$gallery_url = preg_replace('#class url extends url_Core#', 'class url_G3 extends url_Core', $gallery_url); +$gallery_url = str_replace('relative_url().'/'.Router::$current_uri, '/'); - Router::$current_uri = trim(user_chroot::album()->relative_url().'/'.Router::$current_uri, '/'); + } else if( is_null(Router::$controller) && Router::$current_uri != '' ) { + // Non-root album requested + Router::$current_uri = trim(user_chroot::album()->relative_url().'/'.Router::$current_uri, '/'); + } } return parent::parse_url(); } + /** + * Remove the chroot part of the URI. + */ static function site($uri = '', $protocol = FALSE) { if( user_chroot::album() ) { $uri = preg_replace('#^'.user_chroot::album()->relative_url().'#', '', $uri); From f4cc3ba8867a19247d50edc1f750ff407fbbb965 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 16:09:18 +0100 Subject: [PATCH 102/300] Comments --- 3.0/modules/user_chroot/helpers/MY_access.php | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_access.php b/3.0/modules/user_chroot/helpers/MY_access.php index 5f73b4ca..e1a854f8 100644 --- a/3.0/modules/user_chroot/helpers/MY_access.php +++ b/3.0/modules/user_chroot/helpers/MY_access.php @@ -21,22 +21,27 @@ class access extends access_Core { /** - * Does the active user have this permission on this item? - * - * @param string $perm_name - * @param Item_Model $item - * @return boolean + * If the user is chrooted, deny access outside of the chroot. + */ + static function user_can($user, $perm_name, $item) { + if( $user->id == identity::active_user()->id && user_chroot::album() ) { + if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { + return false; + } + } + + return parent::user_can($user, $perm_name, $item); + } + + /** + * Copied from modules/gallery/helpers/access.php because of the usage of self:: */ static function can($perm_name, $item) { return self::user_can(identity::active_user(), $perm_name, $item); } /** - * If the active user does not have this permission, failed with an access::forbidden(). - * - * @param string $perm_name - * @param Item_Model $item - * @return boolean + * Copied from modules/gallery/helpers/access.php because of the usage of self:: */ static function required($perm_name, $item) { if (!self::can($perm_name, $item)) { @@ -48,14 +53,4 @@ class access extends access_Core { } } } - - static function user_can($user, $perm_name, $item) { - if( $user->id == identity::active_user()->id && user_chroot::album() ) { - if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { - return false; - } - } - - return parent::user_can($user, $perm_name, $item); - } } From 117c8b0ee7bae3e986c5f8408612db2ff4c06ec9 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 16:15:56 +0100 Subject: [PATCH 103/300] Comments and code cleanup Overload ORM_MPTT::parent() --- .../helpers/user_chroot_installer.php | 8 ++++---- .../user_chroot/libraries/MY_ORM_MPTT.php | 20 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index f5f597c4..dd92fdf0 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -24,13 +24,13 @@ class user_chroot_installer { */ public static function install() { $db = Database::instance(); - $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( + $db->query('CREATE TABLE IF NOT EXISTS {user_chroots} ( `id` int(9) NOT NULL, `album_id` int(9) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`id`)) - DEFAULT CHARSET=utf8;"); - module::set_version("user_chroot", 1); + DEFAULT CHARSET=utf8;'); + module::set_version('user_chroot', 1); } /** @@ -38,6 +38,6 @@ class user_chroot_installer { */ public static function uninstall() { $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {user_chroots};"); + $db->query('DROP TABLE IF EXISTS {user_chroots};'); } } diff --git a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php index e31124ca..767d2645 100644 --- a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php +++ b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php @@ -19,8 +19,10 @@ */ class ORM_MPTT extends ORM_MPTT_Core { + /** + * Copied from modules/gallery/libraries/ORM_MPTT.php, not sure of the reason... + */ private $model_name = null; - function __construct($id=null) { parent::__construct($id); $this->model_name = inflector::singular($this->table_name); @@ -31,13 +33,13 @@ class ORM_MPTT extends ORM_MPTT_Core { * * @return ORM */ - /*function parent() { + function parent() { if( user_chroot::album() && user_chroot::album()->id == $this->id ) { return null; } else { return parent::parent(); } - }*/ + } /** * Return all the parents of this node, in order from root to this node's immediate parent. @@ -46,14 +48,14 @@ class ORM_MPTT extends ORM_MPTT_Core { */ function parents() { $select = $this - ->where("left_ptr", "<=", $this->left_ptr) - ->where("right_ptr", ">=", $this->right_ptr) - ->where("id", "<>", $this->id) - ->order_by("left_ptr", "ASC"); + ->where('left_ptr', '<=', $this->left_ptr) + ->where('right_ptr', '>=', $this->right_ptr) + ->where('id', '<>', $this->id) + ->order_by('left_ptr', 'ASC'); if( user_chroot::album() ) { - $select->where("left_ptr", ">=", user_chroot::album()->left_ptr); - $select->where("right_ptr", "<=", user_chroot::album()->right_ptr); + $select->where('left_ptr', '>=', user_chroot::album()->left_ptr); + $select->where('right_ptr', '<=', user_chroot::album()->right_ptr); } return $select->find_all(); From db87b3dce4927af5b6c1a6bde21333d0d5484e75 Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sat, 27 Nov 2010 06:44:32 +0800 Subject: [PATCH 104/300] added amazon s3 module --- .../aws_s3/controllers/admin_aws_s3.php | 124 ++ .../aws_s3/helpers/MY_embedlinks_block.php | 10 + .../aws_s3/helpers/MY_embedlinks_theme.php | 13 + 3.0/modules/aws_s3/helpers/MY_item.php | 23 + 3.0/modules/aws_s3/helpers/aws_s3.php | 173 +++ 3.0/modules/aws_s3/helpers/aws_s3_event.php | 34 + .../aws_s3/helpers/aws_s3_installer.php | 40 + 3.0/modules/aws_s3/helpers/aws_s3_task.php | 78 + 3.0/modules/aws_s3/lib/s3.php | 1365 +++++++++++++++++ 3.0/modules/aws_s3/models/MY_Item_Model.php | 40 + 3.0/modules/aws_s3/module.info | 3 + .../aws_s3/views/admin_aws_s3.html.php | 13 + 12 files changed, 1916 insertions(+) create mode 100644 3.0/modules/aws_s3/controllers/admin_aws_s3.php create mode 100644 3.0/modules/aws_s3/helpers/MY_embedlinks_block.php create mode 100644 3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php create mode 100644 3.0/modules/aws_s3/helpers/MY_item.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_event.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_installer.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_task.php create mode 100644 3.0/modules/aws_s3/lib/s3.php create mode 100644 3.0/modules/aws_s3/models/MY_Item_Model.php create mode 100644 3.0/modules/aws_s3/module.info create mode 100644 3.0/modules/aws_s3/views/admin_aws_s3.html.php diff --git a/3.0/modules/aws_s3/controllers/admin_aws_s3.php b/3.0/modules/aws_s3/controllers/admin_aws_s3.php new file mode 100644 index 00000000..8ce2e7ea --- /dev/null +++ b/3.0/modules/aws_s3/controllers/admin_aws_s3.php @@ -0,0 +1,124 @@ +_get_s3_form(); + + if (request::method() == "post") { + access::verify_csrf(); + + if ($form->validate()) { + module::set_var("aws_s3", "enabled", (isset($_POST['enabled']) ? true : false)); + module::set_var("aws_s3", "access_key", $_POST['access_key']); + module::set_var("aws_s3", "secret_key", $_POST['secret_key']); + module::set_var("aws_s3", "bucket_name", $_POST['bucket_name']); + module::set_var("aws_s3", "g3id", $_POST['g3id']); + + module::set_var("aws_s3", "url_str", $_POST['url_str']); + module::set_var("aws_s3", "sig_exp", $_POST['sig_exp']); + + module::set_var("aws_s3", "use_ssl", (isset($_POST['use_ssl']) ? true : false)); + + if (module::get_var("aws_s3", "enabled") && !module::get_var("aws_s3", "synced", false)) + site_status::warning( + t('Your site has not yet been syncronised with your Amazon S3 bucket. Content will not appear correctly until you perform syncronisation. Fix this now', + array("url" => html::mark_clean(url::site("admin/maintenance/start/aws_s3_task::sync?csrf=__CSRF__"))) + ), "aws_s3_not_synced"); + + + message::success(t("Settings have been saved")); + url::redirect("admin/aws_s3"); + } + else { + message::error(t("There was a problem with the submitted form. Please check your values and try again.")); + } + } + + $v = new Admin_View("admin.html"); + $v->page_title = t("Amazon S3 Configuration"); + $v->content = new View("admin_aws_s3.html"); + $v->content->form = $form; + $v->content->end = ""; + + echo $v; + } + + private function _get_s3_form() { + $form = new Forge("admin/aws_s3", "", "post", array("id" => "g-admin-s3-form")); + + $group = $form->group("aws_s3")->label(t("Amazon S3 Settings")); + + $group ->checkbox("enabled") + ->id("s3-enabled") + ->checked(module::get_var("aws_s3", "enabled")) + ->label("S3 enabled"); + + $group ->input("access_key") + ->id("s3-access-key") + ->label("Access Key ID") + ->value(module::get_var("aws_s3", "access_key")) + ->rules("required") + ->error_messages("required", "This field is required") + ->message('Sign up to Amazon S3'); + + $group ->input("secret_key") + ->id("s3-secret-key") + ->label("Secret Access Key") + ->value(module::get_var("aws_s3", "secret_key")) + ->rules("required") + ->error_messages("required", "This field is required"); + + $group ->input("bucket_name") + ->id("s3-bucket") + ->label("Bucket Name") + ->value(module::get_var("aws_s3", "bucket_name")) + ->rules("required") + ->error_messages("required", "This field is required") + ->message("Note: This module will not create a bucket if it does not already exist. Please ensure you have already created the bucket and the bucket has the correct ACL permissions before continuing."); + + $group ->input("g3id") + ->id("s3-g3id") + ->label("G3 ID") + ->value(module::get_var("aws_s3", "g3id", md5(time()))) + ->rules("required") + ->error_messages("required", "This field is required") + ->message("This field allows for multiple G3 instances running off of a single S3 bucket."); + + $group ->checkbox("use_ssl") + ->id("s3-use-ssl") + ->checked(module::get_var("aws_s3", "use_ssl")) + ->label("Use SSL for S3 transfers"); + + $group = $form->group("cdn_settings")->label(t("CDN Settings")); + + $group ->input("url_str") + ->id("s3-url-str") + ->label("URL String") + ->value(module::get_var("aws_s3", "url_str", "http://{bucket}.s3.amazonaws.com/g3/{guid}/{resource}")) + ->rules("required") + ->message("Configure the URL to access uploaded resources on the CDN. Use the following variables to define and build up the URL:
                        +• {bucket} - Bucket Name
                        +• {guid} - Unique identifier for this gallery installation
                        +• {resource} - The end path to the resource/object"); + + $group ->input("sig_exp") + ->id("sig_exp") + ->label("Private Content Signature Duration") + ->value(module::get_var("aws_s3", "sig_exp", 60)) + ->rules("required") + ->callback("aws_s3::validate_number") + ->error_messages("not_numeric", "The value provided is not numeric. Please enter a number in this field.") + ->message("Set the time in seconds for the generated signature for access to permission-restricted S3 objects

                        +Note: this module does not yet support the creation of signatures to access private objects on S3 via CloudFront CDN."); + + $form ->submit("save") + ->value("Save Settings"); + + + return $form; + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php new file mode 100644 index 00000000..fe251b1c --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php @@ -0,0 +1,10 @@ +item && $theme->item->view_1 == 1) + parent::get($block_id, $theme); + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php new file mode 100644 index 00000000..5313cf79 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php @@ -0,0 +1,13 @@ +item; + if ($item->view_1 == 1) + return parent::photo_bottom($theme); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_item.php b/3.0/modules/aws_s3/helpers/MY_item.php new file mode 100644 index 00000000..f26aa431 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_item.php @@ -0,0 +1,23 @@ +parent(); + if ($parent->id > 1) { + aws_s3::upload_album_cover($parent); + } + } + + static function remove_album_cover($album) { + parent::remove_album_cover($album); + + if ($album->id > 1) { + aws_s3::remove_album_cover($album); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3.php b/3.0/modules/aws_s3/helpers/aws_s3.php new file mode 100644 index 00000000..9589f495 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3.php @@ -0,0 +1,173 @@ + 0) + return $matches[1]; + return false; + } + + static function log($item) { + if (is_string($item) || is_numeric($item)) {} + else + $item = print_r($item, true); + + $fh = fopen(VARPATH . "modules/aws_s3/log/aws_s3-" . date("Y-m-d") . ".log", "a"); + fwrite($fh, date("Y-m-d H:i:s") . ": " . $item . "\n"); + fclose($fh); + } + + static function upload_item($item) { + self::get_s3(); + + $success_fs = S3::putObjectFile(VARPATH . "albums/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + $success_th = S3::putObjectFile(VARPATH . "thumbs/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + $success_rs = S3::putObjectFile(VARPATH . "resizes/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + + $success = $success_fs && $success_th && $success_rs; + aws_s3::log("item upload success: " . $success); + } + + static function move_item($old_item, $new_item) { + self::get_s3(); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $old_item->relative_path())); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $old_item->relative_path())); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $old_item->relative_path())); + } + + static function remove_item($item) { + self::get_s3(); + + $success_fs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $item->relative_path())); + $success_th = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $item->relative_path())); + $success_rs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $item->relative_path())); + + $success = $success_fs && $success_th && $success_rs; + aws_s3::log("s3 delete success: " . $success); + } + + static function upload_album_cover($album) { + self::get_s3(); + + if (file_exists(VARPATH . "resizes/" . $album->relative_path() . "/.album.jpg")) + $success_rs = S3::putObjectFile(VARPATH . "resizes/" . $album->relative_path() . "/.album.jpg", + module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/rs/" . $album->relative_path() . "/.album.jpg", + ($album->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + else + $success_rs = true; + + if (file_exists(VARPATH . "thumbs/" . $album->relative_path() . "/.album.jpg")) + $success_th = S3::putObjectFile(VARPATH . "thumbs/" . $album->relative_path() . "/.album.jpg", + module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/th/" . $album->relative_path() . "/.album.jpg", + ($album->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + else + $success_th = true; + + $success = $success_rs && $success_th; + aws_s3::log("album cover upload success: " . $success); + } + + static function remove_album_cover($album) { + self::get_s3(); + + $success_th = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/th/" . $album->relative_path() . "/.album.jpg"); + $success_rs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/rs/" . $album->relative_path() . "/.album.jpg"); + + $success = $success_rs && $success_th; + aws_s3::log("album cover removal success: " . $success); + } + + static function getAuthenticatedURL($bucket, $uri) { + self::get_s3(); + + return S3::getAuthenticatedURL($bucket, $uri, 60); + } + + static function validate_number($field) { + if (preg_match("/\D/", $field->value)) + $field->add_error("not_numeric", 1); + } + + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3_event.php b/3.0/modules/aws_s3/helpers/aws_s3_event.php new file mode 100644 index 00000000..bb46de80 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3_event.php @@ -0,0 +1,34 @@ +get("settings_menu") + ->append( + Menu::factory("link") + ->id("aws_s3_link") + ->label(t("Amazon S3")) + ->url(url::site("admin/aws_s3")) + ); + } + + static function item_created($item) { + if ($item->is_album()) + return true; + + aws_s3::log("Item created - " . $item->id); + aws_s3::upload_item($item); + } + + static function item_deleted($item) { + aws_s3::log("Item deleted - " . $item->id); + aws_s3::remove_item($item); + } + + static function item_moved($new_item, $old_item) { + aws_s3::log("Item moved - " . $item->id); + aws_s3::move_item($old_item, $new_item); + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3_installer.php b/3.0/modules/aws_s3/helpers/aws_s3_installer.php new file mode 100644 index 00000000..75b401b3 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3_installer.php @@ -0,0 +1,40 @@ +callback("aws_s3_task::sync") + ->name(t("Syncronise with Amazon S3")) + ->description(t("Syncronise your Gallery 3 data/images with your Amazon S3 bucket")) + ->severity(log::SUCCESS)); + } + + static function sync($task) { + require_once(MODPATH . "aws_s3/lib/s3.php"); + $s3 = new S3(module::get_var("aws_s3", "access_key"), module::get_var("aws_s3", "secret_key")); + + $mode = $task->get("mode", "init"); + switch ($mode) { + case "init": { + aws_s3::log("re-sync task started.."); + batch::start(); + $items = ORM::factory("item")->find_all(); + aws_s3::log("items to sync: " . count($items)); + $task->set("total_count", count($items)); + $task->set("completed", 0); + $task->set("mode", "empty"); + $task->status = "Emptying contents of bucket"; + } break; + case "empty": { // 0 - 10% + aws_s3::log("emptying bucket contents (any files that may already exist in the bucket/prefix path)"); + $bucket = module::get_var("aws_s3", "bucket_name"); + + $resource = aws_s3::get_resource_url(""); + $stuff = array_reverse(S3::getBucket($bucket, $resource)); + foreach ($stuff as $uri => $item) { + aws_s3::log("removing: " . $uri); + S3::deleteObject($bucket, $uri); + } + $task->percent_complete = 10; + $task->set("mode", "upload"); + $task->state = "Commencing upload..."; + } break; + case "upload": { // 10 - 100% + $completed = $task->get("completed", 0); + $items = ORM::factory("item")->find_all(1, $completed); + foreach ($items as $item) { + if ($item->id > 1) { + aws_s3::log("uploading item " . $item->id . " (" . ($completed + 1) . "/" . $task->get("total_count") . ")"); + if ($item->is_album()) + aws_s3::upload_album_cover($item); + else + aws_s3::upload_item($item); + } + $completed++; + } + $task->set("completed", $completed); + $task->percent_complete = round(90 * ($completed / $task->get("total_count"))) + 10; + $task->status = $completed . " of " . $task->get("total_count"). " uploaded."; + + if ($completed == $task->get("total_count")) { + $task->set("mode", "finish"); + } + } break; + case "finish": { + aws_s3::log("completing upload task.."); + $task->percent_complete = 100; + $task->state = "success"; + $task->done = true; + $task->status = "Sync task completed successfully"; + batch::stop(); + module::set_var("aws_s3", "synced", true); + site_status::clear("aws_s3_not_synced"); + } break; + } + + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/lib/s3.php b/3.0/modules/aws_s3/lib/s3.php new file mode 100644 index 00000000..ae9aeb83 --- /dev/null +++ b/3.0/modules/aws_s3/lib/s3.php @@ -0,0 +1,1365 @@ +getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + $results = array(); + if (!isset($rest->body->Buckets)) return $results; + + if ($detailed) { + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) + $results['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID + ); + $results['buckets'] = array(); + foreach ($rest->body->Buckets->Bucket as $b) + $results['buckets'][] = array( + 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) + ); + } else + foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; + + return $results; + } + + + /* + * Get contents for a bucket + * + * If maxKeys is null this method will loop through truncated result sets + * + * @param string $bucket Bucket name + * @param string $prefix Prefix + * @param string $marker Marker (last file listed) + * @param string $maxKeys Max keys (maximum number of keys to return) + * @param string $delimiter Delimiter + * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes + * @return array | false + */ + public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); + if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + $response = $rest->getResponse(); + if ($response->error === false && $response->code !== 200) + $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); + if ($response->error !== false) { + trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING); + return false; + } + + $results = array(); + + $nextMarker = null; + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->IsTruncated) && + (string)$response->body->IsTruncated == 'false') return $results; + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + // Loop through truncated results if maxKeys isn't specified + if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') + do { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + $rest->setParameter('marker', $nextMarker); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + + if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break; + + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + } while ($response !== false && (string)$response->body->IsTruncated == 'true'); + + return $results; + } + + + /** + * Put a bucket + * + * @param string $bucket Bucket name + * @param constant $acl ACL flag + * @param string $location Set as "EU" to create buckets hosted in Europe + * @return boolean + */ + public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { + $rest = new S3Request('PUT', $bucket, ''); + $rest->setAmzHeader('x-amz-acl', $acl); + + if ($location !== false) { + $dom = new DOMDocument; + $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); + $locationConstraint = $dom->createElement('LocationConstraint', strtoupper($location)); + $createBucketConfiguration->appendChild($locationConstraint); + $dom->appendChild($createBucketConfiguration); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + } + $rest = $rest->getResponse(); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Delete an empty bucket + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function deleteBucket($bucket) { + $rest = new S3Request('DELETE', $bucket); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Create input info array for putObject() + * + * @param string $file Input file + * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) + * @return array | false + */ + public static function inputFile($file, $md5sum = true) { + if (!file_exists($file) || !is_file($file) || !is_readable($file)) { + trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING); + return false; + } + return array('file' => $file, 'size' => filesize($file), + 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : + base64_encode(md5_file($file, true))) : ''); + } + + + /** + * Create input array info for putObject() with a resource + * + * @param string $resource Input resource to read from + * @param integer $bufferSize Input byte size + * @param string $md5sum MD5 hash to send (optional) + * @return array | false + */ + public static function inputResource(&$resource, $bufferSize, $md5sum = '') { + if (!is_resource($resource) || $bufferSize < 0) { + trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING); + return false; + } + $input = array('size' => $bufferSize, 'md5sum' => $md5sum); + $input['fp'] =& $resource; + return $input; + } + + + /** + * Put an object + * + * @param mixed $input Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param array $requestHeaders Array of request headers or content type as a string + * @return boolean + */ + public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + if ($input === false) return false; + $rest = new S3Request('PUT', $bucket, $uri); + + if (is_string($input)) $input = array( + 'data' => $input, 'size' => strlen($input), + 'md5sum' => base64_encode(md5($input, true)) + ); + + // Data + if (isset($input['fp'])) + $rest->fp =& $input['fp']; + elseif (isset($input['file'])) + $rest->fp = @fopen($input['file'], 'rb'); + elseif (isset($input['data'])) + $rest->data = $input['data']; + + // Content-Length (required) + if (isset($input['size']) && $input['size'] >= 0) + $rest->size = $input['size']; + else { + if (isset($input['file'])) + $rest->size = filesize($input['file']); + elseif (isset($input['data'])) + $rest->size = strlen($input['data']); + } + + // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) + if (is_array($requestHeaders)) + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + elseif (is_string($requestHeaders)) // Support for legacy contentType parameter + $input['type'] = $requestHeaders; + + // Content-Type + if (!isset($input['type'])) { + if (isset($requestHeaders['Content-Type'])) + $input['type'] =& $requestHeaders['Content-Type']; + elseif (isset($input['file'])) + $input['type'] = self::__getMimeType($input['file']); + else + $input['type'] = 'application/octet-stream'; + } + + // We need to post with Content-Length and Content-Type, MD5 is optional + if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { + $rest->setHeader('Content-Type', $input['type']); + if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); + + $rest->setAmzHeader('x-amz-acl', $acl); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->getResponse(); + } else + $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Put an object from a file (legacy function) + * + * @param string $file Input file path + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { + return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Put an object from a string (legacy function) + * + * @param string $string Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { + return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Get an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param mixed $saveTo Filename or resource to write to + * @return mixed + */ + public static function getObject($bucket, $uri, $saveTo = false) { + $rest = new S3Request('GET', $bucket, $uri); + if ($saveTo !== false) { + if (is_resource($saveTo)) + $rest->fp =& $saveTo; + else + if (($rest->fp = @fopen($saveTo, 'wb')) !== false) + $rest->file = realpath($saveTo); + else + $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); + } + if ($rest->response->error === false) $rest->getResponse(); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", + $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return $rest->response; + } + + + /** + * Get object information + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param boolean $returnInfo Return response information + * @return mixed | false + */ + public static function getObjectInfo($bucket, $uri, $returnInfo = true) { + $rest = new S3Request('HEAD', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; + } + + + /** + * Copy an object + * + * @param string $bucket Source bucket name + * @param string $uri Source object URI + * @param string $bucket Destination bucket name + * @param string $uri Destination object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Optional array of x-amz-meta-* headers + * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) + * @return mixed | false + */ + public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setHeader('Content-Length', 0); + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->setAmzHeader('x-amz-acl', $acl); + $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, $srcUri)); + if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) + $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return isset($rest->body->LastModified, $rest->body->ETag) ? array( + 'time' => strtotime((string)$rest->body->LastModified), + 'hash' => substr((string)$rest->body->ETag, 1, -1) + ) : false; + } + + + /** + * Set logging for a bucket + * + * @param string $bucket Bucket name + * @param string $targetBucket Target bucket (where logs are stored) + * @param string $targetPrefix Log prefix (e,g; domain.com-) + * @return boolean + */ + public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) { + // The S3 log delivery group has to be added to the target bucket's ACP + if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) { + // Only add permissions to the target bucket when they do not exist + $aclWriteSet = false; + $aclReadSet = false; + foreach ($acp['acl'] as $acl) + if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') { + if ($acl['permission'] == 'WRITE') $aclWriteSet = true; + elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; + } + if (!$aclWriteSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' + ); + if (!$aclReadSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' + ); + if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp); + } + + $dom = new DOMDocument; + $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); + $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); + if ($targetBucket !== null) { + if ($targetPrefix == null) $targetPrefix = $bucket . '-'; + $loggingEnabled = $dom->createElement('LoggingEnabled'); + $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); + $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); + // TODO: Add TargetGrants? + $bucketLoggingStatus->appendChild($loggingEnabled); + } + $dom->appendChild($bucketLoggingStatus); + + $rest = new S3Request('PUT', $bucket, ''); + $rest->setParameter('logging', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get logging status for a bucket + * + * This will return false if logging is not enabled. + * Note: To enable logging, you also need to grant write access to the log group + * + * @param string $bucket Bucket name + * @return array | false + */ + public static function getBucketLogging($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('logging', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + if (!isset($rest->body->LoggingEnabled)) return false; // No logging + return array( + 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, + 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, + ); + } + + + /** + * Disable bucket logging + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function disableBucketLogging($bucket) { + return self::setBucketLogging($bucket, null); + } + + + /** + * Get a bucket's location + * + * @param string $bucket Bucket name + * @return string | false + */ + public static function getBucketLocation($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('location', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; + } + + + /** + * Set object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) + * @return boolean + */ + public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) { + $dom = new DOMDocument; + $dom->formatOutput = true; + $accessControlPolicy = $dom->createElement('AccessControlPolicy'); + $accessControlList = $dom->createElement('AccessControlList'); + + // It seems the owner has to be passed along too + $owner = $dom->createElement('Owner'); + $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); + $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); + $accessControlPolicy->appendChild($owner); + + foreach ($acp['acl'] as $g) { + $grant = $dom->createElement('Grant'); + $grantee = $dom->createElement('Grantee'); + $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted) + $grantee->setAttribute('xsi:type', 'CanonicalUser'); + $grantee->appendChild($dom->createElement('ID', $g['id'])); + } elseif (isset($g['email'])) { // AmazonCustomerByEmail + $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); + $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); + } elseif ($g['type'] == 'Group') { // Group + $grantee->setAttribute('xsi:type', 'Group'); + $grantee->appendChild($dom->createElement('URI', $g['uri'])); + } + $grant->appendChild($grantee); + $grant->appendChild($dom->createElement('Permission', $g['permission'])); + $accessControlList->appendChild($grant); + } + + $accessControlPolicy->appendChild($accessControlList); + $dom->appendChild($accessControlPolicy); + + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setParameter('acl', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed | false + */ + public static function getAccessControlPolicy($bucket, $uri = '') { + $rest = new S3Request('GET', $bucket, $uri); + $rest->setParameter('acl', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + + $acp = array(); + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) { + $acp['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName + ); + } + if (isset($rest->body->AccessControlList)) { + $acp['acl'] = array(); + foreach ($rest->body->AccessControlList->Grant as $grant) { + foreach ($grant->Grantee as $grantee) { + if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser + $acp['acl'][] = array( + 'type' => 'CanonicalUser', + 'id' => (string)$grantee->ID, + 'name' => (string)$grantee->DisplayName, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail + $acp['acl'][] = array( + 'type' => 'AmazonCustomerByEmail', + 'email' => (string)$grantee->EmailAddress, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->URI)) // Group + $acp['acl'][] = array( + 'type' => 'Group', + 'uri' => (string)$grantee->URI, + 'permission' => (string)$grant->Permission + ); + else continue; + } + } + } + return $acp; + } + + + /** + * Delete an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return boolean + */ + public static function deleteObject($bucket, $uri) { + $rest = new S3Request('DELETE', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteObject(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a query string authenticated URL + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param integer $lifetime Lifetime in seconds + * @param boolean $hostBucket Use the bucket name as the hostname + * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) + * @return string + */ + public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) { + $expires = time() + $lifetime; + $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea) + return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', + $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires, + urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); + } + + /** + * Get upload POST parameters for form uploads + * + * @param string $bucket Bucket name + * @param string $uriPrefix Object URI prefix + * @param constant $acl ACL constant + * @param integer $lifetime Lifetime in seconds + * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) + * @param string $successRedirect Redirect URL or 200 / 201 status code + * @param array $amzHeaders Array of x-amz-meta-* headers + * @param array $headers Array of request headers or content type as a string + * @param boolean $flashVars Includes additional "Filename" variable posted by Flash + * @return object + */ + public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) { + // Create policy object + $policy = new stdClass; + $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime)); + $policy->conditions = array(); + $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); + $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); + + $obj = new stdClass; // 200 for non-redirect uploads + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $obj->success_action_status = (string)$successRedirect; + else // URL + $obj->success_action_redirect = $successRedirect; + array_push($policy->conditions, $obj); + + array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); + if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); + foreach (array_keys($headers) as $headerKey) + array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); + foreach ($amzHeaders as $headerKey => $headerVal) { + $obj = new stdClass; $obj->{$headerKey} = (string)$headerVal; array_push($policy->conditions, $obj); + } + array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); + $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); + + // Create parameters + $params = new stdClass; + $params->AWSAccessKeyId = self::$__accessKey; + $params->key = $uriPrefix.'${filename}'; + $params->acl = $acl; + $params->policy = $policy; unset($policy); + $params->signature = self::__getHash($params->policy); + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $params->success_action_status = (string)$successRedirect; + else + $params->success_action_redirect = $successRedirect; + foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + return $params; + } + + /** + * Create a CloudFront distribution + * + * @param string $bucket Bucket name + * @param boolean $enabled Enabled (true/false) + * @param array $cnames Array containing CNAME aliases + * @param string $comment Use the bucket name as the hostname + * @return array | false + */ + public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = '') { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('POST', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($bucket.'.s3.amazonaws.com', $enabled, $comment, (string)microtime(true), $cnames); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 201) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) + return self::__parseCloudFrontDistributionConfig($rest->body); + return false; + } + + + /** + * Get CloudFront distribution info + * + * @param string $distributionId Distribution ID from listDistributions() + * @return array | false + */ + public static function getDistribution($distributionId) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getDistribution($distributionId): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Update a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return array | false + */ + public static function updateDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('PUT', '', '2008-06-30/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames']); + $rest->size = strlen($rest->data); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::updateDistribution({$dist['id']}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } else { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Delete a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return boolean + */ + public static function deleteDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a list of CloudFront distributions + * + * @return array + */ + public static function listDistributions() { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listDistributions(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { + $list = array(); + if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { + //$info['marker'] = (string)$rest->body->Marker; + //$info['maxItems'] = (int)$rest->body->MaxItems; + //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; + } + foreach ($rest->body->DistributionSummary as $summary) { + $list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary); + } + return $list; + } + return array(); + } + + + /** + * Get a DistributionConfig DOMDocument + * + * @internal Used to create XML in createDistribution() and updateDistribution() + * @param string $bucket Origin bucket + * @param boolean $enabled Enabled (true/false) + * @param string $comment Comment to append + * @param string $callerReference Caller reference + * @param array $cnames Array of CNAME aliases + * @return string + */ + private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array()) { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $distributionConfig = $dom->createElement('DistributionConfig'); + $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2008-06-30/'); + $distributionConfig->appendChild($dom->createElement('Origin', $bucket)); + $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); + foreach ($cnames as $cname) + $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); + if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); + $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); + $dom->appendChild($distributionConfig); + return $dom->saveXML(); + } + + + /** + * Parse a CloudFront distribution config + * + * @internal Used to parse the CloudFront DistributionConfig node to an array + * @param object &$node DOMNode + * @return array + */ + private static function __parseCloudFrontDistributionConfig(&$node) { + $dist = array(); + if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) { + $dist['id'] = (string)$node->Id; + $dist['status'] = (string)$node->Status; + $dist['time'] = strtotime((string)$node->LastModifiedTime); + $dist['domain'] = (string)$node->DomainName; + } + if (isset($node->CallerReference)) + $dist['callerReference'] = (string)$node->CallerReference; + if (isset($node->Comment)) + $dist['comment'] = (string)$node->Comment; + if (isset($node->Enabled, $node->Origin)) { + $dist['origin'] = (string)$node->Origin; + $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; + } elseif (isset($node->DistributionConfig)) { + $dist = array_merge($dist, self::__parseCloudFrontDistributionConfig($node->DistributionConfig)); + } + if (isset($node->CNAME)) { + $dist['cnames'] = array(); + foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname; + } + return $dist; + } + + + /** + * Grab CloudFront response + * + * @internal Used to parse the CloudFront S3Request::getResponse() output + * @param object &$rest S3Request instance + * @return object + */ + private static function __getCloudFrontResponse(&$rest) { + $rest->getResponse(); + if ($rest->response->error === false && isset($rest->response->body) && + is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); + // Grab CloudFront errors + if (isset($rest->response->body->Error, $rest->response->body->Error->Code, + $rest->response->body->Error->Message)) { + $rest->response->error = array( + 'code' => (string)$rest->response->body->Error->Code, + 'message' => (string)$rest->response->body->Error->Message + ); + unset($rest->response->body); + } + } + return $rest->response; + } + + + /** + * Get MIME type for file + * + * @internal Used to get mime types + * @param string &$file File path + * @return string + */ + public static function __getMimeType(&$file) { + $type = false; + // Fileinfo documentation says fileinfo_open() will use the + // MAGIC env var for the magic file + if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && + ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { + if (($type = finfo_file($finfo, $file)) !== false) { + // Remove the charset and grab the last content-type + $type = explode(' ', str_replace('; charset=', ';charset=', $type)); + $type = array_pop($type); + $type = explode(';', $type); + $type = trim(array_shift($type)); + } + finfo_close($finfo); + + // If anyone is still using mime_content_type() + } elseif (function_exists('mime_content_type')) + $type = trim(mime_content_type($file)); + + if ($type !== false && strlen($type) > 0) return $type; + + // Otherwise do it the old fashioned way + static $exts = array( + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' + ); + $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); + return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; + } + + + /** + * Generate the auth string: "AWS AccessKey:Signature" + * + * @internal Used by S3Request::getResponse() + * @param string $string String to sign + * @return string + */ + public static function __getSignature($string) { + return 'AWS '.self::$__accessKey.':'.self::__getHash($string); + } + + + /** + * Creates a HMAC-SHA1 hash + * + * This uses the hash extension if loaded + * + * @internal Used by __getSignature() + * @param string $string String to sign + * @return string + */ + private static function __getHash($string) { + return base64_encode(extension_loaded('hash') ? + hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1( + (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . + pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^ + (str_repeat(chr(0x36), 64))) . $string))))); + } + +} + +final class S3Request { + private $verb, $bucket, $uri, $resource = '', $parameters = array(), + $amzHeaders = array(), $headers = array( + 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' + ); + public $fp = false, $size = 0, $data = false, $response; + + + /** + * Constructor + * + * @param string $verb Verb + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed + */ + function __construct($verb, $bucket = '', $uri = '', $defaultHost = 's3.amazonaws.com') { + $this->verb = $verb; + $this->bucket = strtolower($bucket); + $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; + + if ($this->bucket !== '') { + $this->headers['Host'] = $this->bucket.'.'.$defaultHost; + $this->resource = '/'.$this->bucket.$this->uri; + } else { + $this->headers['Host'] = $defaultHost; + //$this->resource = strlen($this->uri) > 1 ? '/'.$this->bucket.$this->uri : $this->uri; + $this->resource = $this->uri; + } + $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); + + $this->response = new STDClass; + $this->response->error = false; + } + + + /** + * Set request parameter + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setParameter($key, $value) { + $this->parameters[$key] = $value; + } + + + /** + * Set request header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setHeader($key, $value) { + $this->headers[$key] = $value; + } + + + /** + * Set x-amz-meta-* header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setAmzHeader($key, $value) { + $this->amzHeaders[$key] = $value; + } + + + /** + * Get the S3 response + * + * @return object | false + */ + public function getResponse() { + $query = ''; + if (sizeof($this->parameters) > 0) { + $query = substr($this->uri, -1) !== '?' ? '?' : '&'; + foreach ($this->parameters as $var => $value) + if ($value == null || $value == '') $query .= $var.'&'; + // Parameters should be encoded (thanks Sean O'Dea) + else $query .= $var.'='.rawurlencode($value).'&'; + $query = substr($query, 0, -1); + $this->uri .= $query; + + if (array_key_exists('acl', $this->parameters) || + array_key_exists('location', $this->parameters) || + array_key_exists('torrent', $this->parameters) || + array_key_exists('logging', $this->parameters)) + $this->resource .= $query; + } + $url = ((S3::$useSSL && extension_loaded('openssl')) ? + 'https://':'http://').$this->headers['Host'].$this->uri; + //var_dump($this->bucket, $this->uri, $this->resource, $url); + + // Basic setup + $curl = curl_init(); + curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); + + if (S3::$useSSL) { + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); + } + + curl_setopt($curl, CURLOPT_URL, $url); + + // Headers + $headers = array(); $amz = array(); + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + foreach ($this->headers as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + + // Collect AMZ headers for signature + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; + + // AMZ headers must be sorted + if (sizeof($amz) > 0) { + sort($amz); + $amz = "\n".implode("\n", $amz); + } else $amz = ''; + + // Authorization string (CloudFront stringToSign should only contain a date) + $headers[] = 'Authorization: ' . S3::__getSignature( + $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : + $this->verb."\n".$this->headers['Content-MD5']."\n". + $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource + ); + + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + // Request types + switch ($this->verb) { + case 'GET': break; + case 'PUT': case 'POST': // POST only used for CloudFront + if ($this->fp !== false) { + curl_setopt($curl, CURLOPT_PUT, true); + curl_setopt($curl, CURLOPT_INFILE, $this->fp); + if ($this->size >= 0) + curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); + } elseif ($this->data !== false) { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); + if ($this->size >= 0) + curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size); + } else + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + break; + case 'HEAD': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); + curl_setopt($curl, CURLOPT_NOBODY, true); + break; + case 'DELETE': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + default: break; + } + + // Execute, grab errors + if (curl_exec($curl)) + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + else + $this->response->error = array( + 'code' => curl_errno($curl), + 'message' => curl_error($curl), + 'resource' => $this->resource + ); + + @curl_close($curl); + + // Parse body into XML + if ($this->response->error === false && isset($this->response->headers['type']) && + $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { + $this->response->body = simplexml_load_string($this->response->body); + + // Grab S3 errors + if (!in_array($this->response->code, array(200, 204)) && + isset($this->response->body->Code, $this->response->body->Message)) { + $this->response->error = array( + 'code' => (string)$this->response->body->Code, + 'message' => (string)$this->response->body->Message + ); + if (isset($this->response->body->Resource)) + $this->response->error['resource'] = (string)$this->response->body->Resource; + unset($this->response->body); + } + } + + // Clean up file resources + if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); + + return $this->response; + } + + + /** + * CURL write callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseWriteCallback(&$curl, &$data) { + if ($this->response->code == 200 && $this->fp !== false) + return fwrite($this->fp, $data); + else + $this->response->body .= $data; + return strlen($data); + } + + + /** + * CURL header callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseHeaderCallback(&$curl, &$data) { + if (($strlen = strlen($data)) <= 2) return $strlen; + if (substr($data, 0, 4) == 'HTTP') + $this->response->code = (int)substr($data, 9, 3); + else { + list($header, $value) = explode(': ', trim($data), 2); + if ($header == 'Last-Modified') + $this->response->headers['time'] = strtotime($value); + elseif ($header == 'Content-Length') + $this->response->headers['size'] = (int)$value; + elseif ($header == 'Content-Type') + $this->response->headers['type'] = $value; + elseif ($header == 'ETag') + $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; + elseif (preg_match('/^x-amz-meta-.*$/', $header)) + $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; + } + return $strlen; + } + +} diff --git a/3.0/modules/aws_s3/models/MY_Item_Model.php b/3.0/modules/aws_s3/models/MY_Item_Model.php new file mode 100644 index 00000000..4cacc2fc --- /dev/null +++ b/3.0/modules/aws_s3/models/MY_Item_Model.php @@ -0,0 +1,40 @@ +is_photo()) { + return aws_s3::generate_url("th/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + else if ($this->is_album() && $this->id > 1) { + return aws_s3::generate_url("th/" . $this->relative_path() . "/.album.jpg", ($this->view_1 == 1 ? false : true), $this->updated); + } + else if ($this->is_movie()) { + $relative_path = preg_replace("/...$/", "jpg", $this->relative_path()); + return aws_s3::generate_url("th/" . $relative_path, ($this->view_1 == 1 ? false : true), $this->updated); + } + } + + public function file_url($full_uri=false) { + if (!module::get_var("aws_s3", "enabled")) + return parent::file_url($full_uri); + + return aws_s3::generate_url("fs/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + + public function resize_url($full_uri=false) { + if (!module::get_var("aws_s3", "enabled")) + return parent::resize_url($full_uri); + + if ($this->is_album() && $this->id > 1) { + return aws_s3::generate_url("rs/" . $this->relative_path() . "/.album.jpg", ($this->view_1 == 1 ? false : true), $this->updated); + } + else { + return aws_s3::generate_url("rs/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/module.info b/3.0/modules/aws_s3/module.info new file mode 100644 index 00000000..6c301507 --- /dev/null +++ b/3.0/modules/aws_s3/module.info @@ -0,0 +1,3 @@ +name = "Amazon S3" +description = "Seamlessly transfer your Gallery data to Amazon S3 CDN for a lightning fast gallery" +version = 1 diff --git a/3.0/modules/aws_s3/views/admin_aws_s3.html.php b/3.0/modules/aws_s3/views/admin_aws_s3.html.php new file mode 100644 index 00000000..961186e3 --- /dev/null +++ b/3.0/modules/aws_s3/views/admin_aws_s3.html.php @@ -0,0 +1,13 @@ + + +
                        +

                        + +

                        + +
                        + +
                        +
                        + + From a72d847278865cb8cbb672a8220e52778bd86e44 Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sun, 28 Nov 2010 19:59:40 +0800 Subject: [PATCH 105/300] added aws_s3 module --- .../aws_s3/helpers/MY_embedlinks_block.php | 20 ++++++++++++++++++- .../aws_s3/helpers/MY_embedlinks_theme.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/MY_item.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3_event.php | 20 ++++++++++++++++++- .../aws_s3/helpers/aws_s3_installer.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3_task.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/models/MY_Item_Model.php | 20 ++++++++++++++++++- 8 files changed, 152 insertions(+), 8 deletions(-) diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php index fe251b1c..e9ca1611 100644 --- a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php @@ -1,4 +1,22 @@ - Date: Sun, 28 Nov 2010 19:18:42 +0800 Subject: [PATCH 106/300] - Code cleanup - Fix some bugs - Serious performances improvement when building the tree (regression: tree is not alphabetically ordered) --- .../user_chroot/helpers/user_chroot_event.php | 122 +++++++++--------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_event.php b/3.0/modules/user_chroot/helpers/user_chroot_event.php index c43c6dc4..51027a44 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_event.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_event.php @@ -21,92 +21,94 @@ class user_chroot_event_Core { /** - * Called just before a user is deleted. This will remove the user from - * the user_chroot directory. + * Called just before a user deletion. */ - static function user_before_delete($user) { - ORM::factory("user_chroot")->delete($user->id); + public static function user_before_delete($user) { + ORM::factory('user_chroot', $user->id)->delete(); } /** - * Called when admin is adding a user + * Called just before an item deletion. + */ + public static function item_before_delete($item) { + if( $item->is_album() ) { + ORM::factory('user_chroot')->where('album_id', '=', $item->id)->delete(); + } + } + + /** + * Called when building the 'Add user' form for an admin. */ static function user_add_form_admin($user, $form) { - $form->add_user->dropdown("user_chroot") + $form->add_user->dropdown('user_chroot') ->label(t("Root Album")) - ->options(self::createGalleryArray()) - ->selected(0); + ->options(self::albumsTreeArray()) + ->selected(1); } /** - * Called after a user has been added + * Called just after a user has been added by an admin. */ - static function user_add_form_admin_completed($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - $user_chroot->id = $user->id; - $user_chroot->album_id = $form->add_user->user_chroot->value; - $user_chroot->save(); - } - - /** - * Called when admin is editing a user - */ - static function user_edit_form_admin($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - if ($user_chroot->loaded()) { - $selected = $user_chroot->album_id; - } else { - $selected = 0; + public static function user_add_form_admin_completed($user, $form) { + if( $form->add_user->user_chroot->value > 1 ) { + $user_chroot = ORM::factory('user_chroot'); + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->add_user->user_chroot->value; + $user_chroot->save(); } - $form->edit_user->dropdown("user_chroot") + } + + /** + * Called when building the 'Edit user' form for an admin. + */ + public static function user_edit_form_admin($user, $form) { + $user_chroot = ORM::factory('user_chroot', $user->id); + + $selected = ( $user_chroot->loaded() ) + ? $user_chroot->album_id + : 1; + + $form->edit_user->dropdown('user_chroot') ->label(t("Root Album")) - ->options(self::createGalleryArray()) + ->options(self::albumsTreeArray()) ->selected($selected); } /** - * Called after a user had been edited by the admin + * Called just after a user has been edited by an admin. */ - static function user_edit_form_admin_completed($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - if ($user_chroot->loaded()) { - $user_chroot->album_id = $form->edit_user->user_chroot->value; + public static function user_edit_form_admin_completed($user, $form) { + if( $form->edit_user->user_chroot->value <= 1 ) { + ORM::factory('user_chroot')->delete($user->id); + } else { - $user_chroot->id = $user->id; + $user_chroot = ORM::factory('user_chroot', $user->id); + + if( !$user_chroot->loaded() ) { + $user_chroot = ORM::factory('user_chroot'); + $user_chroot->id = $user->id; + } + $user_chroot->album_id = $form->edit_user->user_chroot->value; + $user_chroot->save(); } - $user_chroot->save(); - } - - - /** - * Creates an array of galleries - */ - static function createGalleryArray() { - $array[0] = "none"; - $root = ORM::factory("item", 1); - self::tree($root, "", $array); - return $array; } /** - * recursive function to build array for drop down list + * Generate an array representing the hierarchy of albums. */ - static function tree($parent, $dashes, &$array) { - if ($parent->id == "1") { - $array[$parent->id] = ORM::factory("item", 1)->title; - } else { - $array[$parent->id] = "$dashes $parent->name"; - } - - $albums = ORM::factory("item") - ->where("parent_id", "=", $parent->id) - ->where("type", "=", "album") - ->order_by("title", "ASC") + private static function albumsTreeArray($level_marker = '    ') { + $tree = array(); + $albums = ORM::factory('item') + ->where('type', '=', 'album') + ->order_by('left_ptr', 'ASC') ->find_all(); - foreach ($albums as $album) { - self::tree($album, "-$dashes", $array); + + foreach($albums as $album) { + $tree[$album->id] = html::clean( + str_repeat($level_marker, $album->level - 1).' '.$album->title ); } - return; + + return $tree; } } From 2594ffdbea92526b5bd6b0a67be992aa6d78ec56 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 19:22:01 +0800 Subject: [PATCH 107/300] Comments --- 3.0/modules/user_chroot/helpers/user_chroot_installer.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index ee89fb13..f12a2ed1 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -19,6 +19,9 @@ */ class user_chroot_installer { + /** + * Create the table user_chroot when installing the module. + */ static function install() { $db = Database::instance(); $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( @@ -31,7 +34,7 @@ class user_chroot_installer { } /** - * Drops the table of user chroot when the module is uninstalled. + * Drops the table user_chroot when uninstalling the module. */ static function uninstall() { $db = Database::instance(); From cc31d8b85ae988b878786f0c7c42b77f80f4a0a0 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 19:23:35 +0800 Subject: [PATCH 108/300] Explicit is better than implicit... --- 3.0/modules/user_chroot/helpers/user_chroot_installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index f12a2ed1..f5f597c4 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -22,7 +22,7 @@ class user_chroot_installer { /** * Create the table user_chroot when installing the module. */ - static function install() { + public static function install() { $db = Database::instance(); $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( `id` int(9) NOT NULL, @@ -36,7 +36,7 @@ class user_chroot_installer { /** * Drops the table user_chroot when uninstalling the module. */ - static function uninstall() { + public static function uninstall() { $db = Database::instance(); $db->query("DROP TABLE IF EXISTS {user_chroots};"); } From 9d19c056520eea182f9197a36e2feef2b4291bd8 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 19:37:57 +0800 Subject: [PATCH 109/300] - Code cleanup - Comments --- 3.0/modules/user_chroot/helpers/user_chroot.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot.php b/3.0/modules/user_chroot/helpers/user_chroot.php index 4d0a8499..8afa753f 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot.php +++ b/3.0/modules/user_chroot/helpers/user_chroot.php @@ -21,18 +21,21 @@ class user_chroot_Core { private static $_album = null; + /** + * Return the root album of the current user, or false if the user is not + * chrooted. + */ public static function album() { if( is_null(self::$_album) ) { self::$_album = false; - $user = identity::active_user(); + $item = ORM::factory('item') + ->join('user_chroots', 'items.id', 'user_chroots.album_id') + ->where('user_chroots.id', '=', identity::active_user()->id) + ->find(); - $user_chroot = ORM::factory("user_chroot", $user->id); - if( $user_chroot->loaded() && $user_chroot->album_id != 0 ) { - $item = ORM::factory("item", $user_chroot->album_id); - if( $item->loaded() ) { - self::$_album = $item; - } + if( $item->loaded() ) { + self::$_album = $item; } } From 89ef20bba6cc1557e85ccd8ffcf716d7088eb0c7 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:03:57 +0800 Subject: [PATCH 110/300] - Overload a forgotten method - Code cleanup --- 3.0/modules/user_chroot/helpers/MY_item.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_item.php b/3.0/modules/user_chroot/helpers/MY_item.php index 423b8ddc..d6368f18 100644 --- a/3.0/modules/user_chroot/helpers/MY_item.php +++ b/3.0/modules/user_chroot/helpers/MY_item.php @@ -24,11 +24,17 @@ class item extends item_Core { if( user_chroot::album() ) { $model->and_open() - ->and_where("items.left_ptr", ">=", user_chroot::album()->left_ptr) - ->and_where("items.right_ptr", "<=", user_chroot::album()->right_ptr) + ->and_where('items.left_ptr', '>=', user_chroot::album()->left_ptr) + ->and_where('items.right_ptr', '<=', user_chroot::album()->right_ptr) ->close(); } return $model; } + + static function root() { + return ( user_chroot::album() ) + ? user_chroot::album() + : parent::root(); + } } From ebf1f4bacf3c1dc25f45fc541c8fbcab6e22a70b Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:04:59 +0800 Subject: [PATCH 111/300] - Bugfix - Comments - Code cleanup --- 3.0/modules/user_chroot/helpers/MY_url.php | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_url.php b/3.0/modules/user_chroot/helpers/MY_url.php index 31818fa4..b693c9c5 100644 --- a/3.0/modules/user_chroot/helpers/MY_url.php +++ b/3.0/modules/user_chroot/helpers/MY_url.php @@ -18,25 +18,40 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -// /!\ Hack: There is no good way to extend gallery url +/* + * /!\ Hack + * Module 'gallery' already provides a class 'url' which extends url_Core. This + * hack renames class 'url', so user_chroot can have its own (which extends the + * original) + */ $gallery_url = file_get_contents(MODPATH . 'gallery/helpers/MY_url.php'); -$gallery_url = preg_replace('#^<\?php #', '', $gallery_url); -$gallery_url = preg_replace('#class url extends url_Core#', 'class url_G3 extends url_Core', $gallery_url); +$gallery_url = str_replace('relative_url().'/'.Router::$current_uri, '/'); - Router::$current_uri = trim(user_chroot::album()->relative_url().'/'.Router::$current_uri, '/'); + } else if( is_null(Router::$controller) && Router::$current_uri != '' ) { + // Non-root album requested + Router::$current_uri = trim(user_chroot::album()->relative_url().'/'.Router::$current_uri, '/'); + } } return parent::parse_url(); } + /** + * Remove the chroot part of the URI. + */ static function site($uri = '', $protocol = FALSE) { if( user_chroot::album() ) { $uri = preg_replace('#^'.user_chroot::album()->relative_url().'#', '', $uri); From f81c7c1ece6b5fe2a924666d63f13344e6dfe56b Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:09:18 +0800 Subject: [PATCH 112/300] Comments --- 3.0/modules/user_chroot/helpers/MY_access.php | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_access.php b/3.0/modules/user_chroot/helpers/MY_access.php index 5f73b4ca..e1a854f8 100644 --- a/3.0/modules/user_chroot/helpers/MY_access.php +++ b/3.0/modules/user_chroot/helpers/MY_access.php @@ -21,22 +21,27 @@ class access extends access_Core { /** - * Does the active user have this permission on this item? - * - * @param string $perm_name - * @param Item_Model $item - * @return boolean + * If the user is chrooted, deny access outside of the chroot. + */ + static function user_can($user, $perm_name, $item) { + if( $user->id == identity::active_user()->id && user_chroot::album() ) { + if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { + return false; + } + } + + return parent::user_can($user, $perm_name, $item); + } + + /** + * Copied from modules/gallery/helpers/access.php because of the usage of self:: */ static function can($perm_name, $item) { return self::user_can(identity::active_user(), $perm_name, $item); } /** - * If the active user does not have this permission, failed with an access::forbidden(). - * - * @param string $perm_name - * @param Item_Model $item - * @return boolean + * Copied from modules/gallery/helpers/access.php because of the usage of self:: */ static function required($perm_name, $item) { if (!self::can($perm_name, $item)) { @@ -48,14 +53,4 @@ class access extends access_Core { } } } - - static function user_can($user, $perm_name, $item) { - if( $user->id == identity::active_user()->id && user_chroot::album() ) { - if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { - return false; - } - } - - return parent::user_can($user, $perm_name, $item); - } } From b7bb8a50b0f30c9fa2a95a4c0c025bac1db90200 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:15:56 +0800 Subject: [PATCH 113/300] Comments and code cleanup Overload ORM_MPTT::parent() --- .../helpers/user_chroot_installer.php | 8 ++++---- .../user_chroot/libraries/MY_ORM_MPTT.php | 20 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index f5f597c4..dd92fdf0 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -24,13 +24,13 @@ class user_chroot_installer { */ public static function install() { $db = Database::instance(); - $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( + $db->query('CREATE TABLE IF NOT EXISTS {user_chroots} ( `id` int(9) NOT NULL, `album_id` int(9) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`id`)) - DEFAULT CHARSET=utf8;"); - module::set_version("user_chroot", 1); + DEFAULT CHARSET=utf8;'); + module::set_version('user_chroot', 1); } /** @@ -38,6 +38,6 @@ class user_chroot_installer { */ public static function uninstall() { $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {user_chroots};"); + $db->query('DROP TABLE IF EXISTS {user_chroots};'); } } diff --git a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php index e31124ca..767d2645 100644 --- a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php +++ b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php @@ -19,8 +19,10 @@ */ class ORM_MPTT extends ORM_MPTT_Core { + /** + * Copied from modules/gallery/libraries/ORM_MPTT.php, not sure of the reason... + */ private $model_name = null; - function __construct($id=null) { parent::__construct($id); $this->model_name = inflector::singular($this->table_name); @@ -31,13 +33,13 @@ class ORM_MPTT extends ORM_MPTT_Core { * * @return ORM */ - /*function parent() { + function parent() { if( user_chroot::album() && user_chroot::album()->id == $this->id ) { return null; } else { return parent::parent(); } - }*/ + } /** * Return all the parents of this node, in order from root to this node's immediate parent. @@ -46,14 +48,14 @@ class ORM_MPTT extends ORM_MPTT_Core { */ function parents() { $select = $this - ->where("left_ptr", "<=", $this->left_ptr) - ->where("right_ptr", ">=", $this->right_ptr) - ->where("id", "<>", $this->id) - ->order_by("left_ptr", "ASC"); + ->where('left_ptr', '<=', $this->left_ptr) + ->where('right_ptr', '>=', $this->right_ptr) + ->where('id', '<>', $this->id) + ->order_by('left_ptr', 'ASC'); if( user_chroot::album() ) { - $select->where("left_ptr", ">=", user_chroot::album()->left_ptr); - $select->where("right_ptr", "<=", user_chroot::album()->right_ptr); + $select->where('left_ptr', '>=', user_chroot::album()->left_ptr); + $select->where('right_ptr', '<=', user_chroot::album()->right_ptr); } return $select->find_all(); From 3901c7b0bb4d77ce46c3d931fcd97b87d33e11f2 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Sun, 5 Dec 2010 19:27:13 +0100 Subject: [PATCH 114/300] Very, very basic and preliminary rest query support.. --- 3.0/modules/unrest/helpers/unrest.php | 22 ++ 3.0/modules/unrest/helpers/unrest_event.php | 8 + 3.0/modules/unrest/helpers/unrest_rest.php | 304 ++++++++++++++++++++ 3.0/modules/unrest/module.info | 3 + 4 files changed, 337 insertions(+) create mode 100644 3.0/modules/unrest/helpers/unrest.php create mode 100644 3.0/modules/unrest/helpers/unrest_event.php create mode 100644 3.0/modules/unrest/helpers/unrest_rest.php create mode 100644 3.0/modules/unrest/module.info diff --git a/3.0/modules/unrest/helpers/unrest.php b/3.0/modules/unrest/helpers/unrest.php new file mode 100644 index 00000000..a132b536 --- /dev/null +++ b/3.0/modules/unrest/helpers/unrest.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/3.0/modules/unrest/helpers/unrest_rest.php b/3.0/modules/unrest/helpers/unrest_rest.php new file mode 100644 index 00000000..c16d4d4f --- /dev/null +++ b/3.0/modules/unrest/helpers/unrest_rest.php @@ -0,0 +1,304 @@ + 'name', + 'description' => 'description' + ); + foreach ($likeMapping as $key => $col) + { + if (isset($request->params->$key)) { $limit[$col] = array('op' => 'LIKE', 'value' => '%' . $request->params->$key . '%'); } + } + + return $limit; + } + + private function getBasicLimiters($request, $limit = array()) + { + $directMapping = array( + 'type' => 'type', + 'id' => 'items.id', + 'parent' => 'parent_id', + 'mime' => 'mime_type'); + foreach ($directMapping as $key => $col) + { + if (isset($request->params->$key)) { $limit[$col] = array('op' => '=', 'value' => unrest_rest::resolveLimitOption($request->params->$key)); } + } + + return $limit; + } + + private function albumsICanAccess() + { + $db = db::build(); + $gids = identity::group_ids_for_active_user(); + $q = $db->select('id')->from('items'); + foreach ($gids as $gid) { $q->or_where('view_' . $gid, '=', 1); } + + $q->where('type', '=', 'album'); + $permitted = array(); + foreach($q->execute() as $row) { $permitted[] = $row->id; } + + return $permitted; + } + + static function queryLimitByPermission(&$query, $permitted) + { + $query->and_open()->and_open()->where('type', '=', 'album')->and_where('items.id', 'IN', $permitted)->close(); + $query->or_open()->where('type', '!=', 'album')->and_where('parent_id', 'IN', $permitted)->close()->close(); + } + + static function baseItemQuery($db) + { + $fields = array( + 'items.id', 'title', 'album_cover_item_id', 'description', 'height', 'width', 'left_ptr', 'right_ptr', + 'level', 'mime_type', 'name', 'owner_id', 'parent_id', 'relative_path_cache', 'relative_url_cache', + 'resize_dirty', 'slug', 'sort_column', 'sort_order', 'thumb_dirty','thumb_height', 'view_1', 'type', + 'resize_height', 'resize_width', 'thumb_height', 'thumb_width', 'slug', 'name', 'relative_path_cache' + ); + + $permfields = array('view_', 'view_full_', 'edit_', 'add_'); + + foreach (identity::group_ids_for_active_user() as $album) + { + foreach ($permfields as $field) + { + $fields[] = $field . $album; + } + } + + return($db->select($fields)->from('items')->join('access_caches', 'access_caches.item_id', 'items.id')); +/* + return($db->select(array( + 'id', 'title', 'album_cover_item_id', 'description', 'height', 'width', 'left_ptr', 'right_ptr', + 'level', 'mime_type', 'name', 'owner_id', 'parent_id', 'relative_path_cache', 'relative_url_cache', + 'resize_dirty', 'slug', 'sort_column', 'sort_order', 'thumb_dirty','thumb_height', 'view_1', 'type', + 'resize_height', 'resize_width', 'thumb_height', 'thumb_width', 'slug', 'name', 'relative_path_cache' + ))->from('items')); + */ + } + + static function queryLimitByLimiter(&$query, $limit) + { + foreach ($limit as $key => $block) + { + if (gettype($block['value']) == 'array') { $query->and_where($key, 'IN', $block['value']); } + else { $query->and_where($key, $block['op'], $block['value']); } + } + } + + static function getDisplayOptions($request) + { + if (isset($request->params->display)) { + return(split(',', $request->params->display)); + } else { + return(array('uiimage','uitext','ownership','members')); + }; + } + + static function queryOrder(&$query, $request) + { + if (isset($request->params->order)) { + $order = $request->params->order; + $direction = 'asc'; + if (isset($request->params->direction)) + { + if ($request->params->direction == 'desc') { $direction = 'desc'; } + } + + switch ($order) + { + case 'tree': + $query->order_by(array('level' => 'ASC', 'left_ptr' => 'ASC')); + break; + case 'created': + $query->order_by(array('created' => $direction)); + break; + case 'updated': + $query->order_by(array('updated' => $direction)); + break; + case 'views': + $query->order_by(array('view_count' => $direction)); + break; + case 'type': + $query->order_by(array('type' => $direction)); + break; + } + } + } + + static function addChildren($request, $db, $filler, $permitted, $display, &$return) + { + $children = $db->select('parent_id', 'id')->from('items')->where('parent_id', 'IN', $filler['children_of']); + if (isset($request->params->childtypes)) + { + $types = split(',', $request->params->childtypes); + $children->where('type', 'IN', $types); + } + + /* We shouldn't have any albums we don't have access to by default in this query, but just in case.. */ + unrest_rest::queryLimitByPermission(&$children, $permitted); + + + $childBlock = array(); + foreach($children->execute() as $item) + { + $childBlock[$item->parent_id][] = intval($item->id); + } + + foreach ($return as &$data) + { + if (array_key_exists($data['entity']['id'], $childBlock)) + { + if (in_array('terse', $display)) { + $data['members'] = $childBlock[ $data['id'] ]; + } + else { + $members = array(); + foreach ($childBlock[ $data['entity']['id'] ] as $child) { + $members[] = unrest_rest::makeRestURL('item', $child); + } + $data['members'] = $members; + } + } + else + { + $data['members'] = array(); + } + } + } + + private function makeRestURL($resource, $identifier) + { + return url::abs_site("rest") . '/' . $resource . '/' . $identifier; + } + + public function size_url($size, $relative_path_cache, $type) { + $base = url::abs_file('var/' . $size . '/' ) . $relative_path_cache; + if ($type == 'photo') { + return $base; + } else if ($type == 'album') { + return $base . "/.album.jpg"; + } else if ($type == 'movie') { + // Replace the extension with jpg + return preg_replace("/...$/", "jpg", $base); + } + } + + + static function get($request) { + $db = db::build(); + + /* Build basic limiters */ + $limit = unrest_rest::getBasicLimiters($request); + $limit = unrest_rest::getFreetextLimiters($request,$limit); + + error_log(print_r($limit,1)); + + /* Build numeric limiters */ + /* ...at some point. */ + + /* Figure out an array of albums we got permissions to access */ + $permitted = unrest_rest::albumsICanAccess(); + + $display = unrest_rest::getDisplayOptions($request); + $items = unrest_rest::baseItemQuery($db); + + /* + Introduce some WHERE statements that'll make sure that we don't get to see stuff we + shouldn't be seeing. + */ + unrest_rest::queryLimitByPermission(&$items, $permitted); + unrest_rest::queryLimitByLimiter(&$items, $limit); + unrest_rest::queryOrder(&$items, $request); + + $return = array(); + $filler = array(); + $relationshipCandidates = array(); + + foreach($items->execute() as $item) + { + $data = array( + 'id' => intval($item->id), + 'parent' => intval($item->parent_id), + 'owner_id' => intval($item->{'owner_id'}), + 'public' => ($item->view_1)?true:false, + 'type' => $item->type // Grmbl + ); + + if (in_array('uitext', $display)) { + $ui = array( + 'title' => $item->title, + 'description' => $item->description, + 'name' => $item->name, + 'slug' => $item->slug + ); + + $data = array_merge($data, $ui); + } + + + if (in_array('uiimage', $display)) { + $ui = array( + 'height' => $item->height, + 'width' => $item->width, + 'resize_height' => $item->resize_height, + 'resize_width' => $item->resize_width, + 'thumb_height' => $item->resize_height, + 'thumb_width' => $item->resize_width + ); + + $ui['thumb_url_public'] = unrest_rest::size_url('thumbs', $item->relative_path_cache, $item->type); + $public = $item->view_1?true:false; + $fullPublic = $item->view_full_1?true:false; + + if ($item->type != 'album') + { + $ui['file_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=full'); + $ui['thumb_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=thumb'); + $ui['resize_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=resize'); + + if ($public) { + $ui['resize_url_public'] = unrest_rest::size_url('resizes', $item->relative_path_cache, $item->type); + + if ($fullPublic) { + $ui['file_url_public'] = unrest_rest::size_url('albums', $item->relative_path_cache, $item->type); + } + } + } + + $data = array_merge($data, $ui); + } + + if (in_array('members', $display)) { + $filler['children_of'][] = $item->id; + } + + $return[] = array( + 'url' => unrest_rest::makeRestURL('item', $item->id ), + 'entity' => $data + ); + } + + + /* Do we need to fetch children? */ + if (array_key_exists('children_of', $filler)) + { + unrest_rest::addChildren($request, $db, $filler, $permitted, $display, &$return); + } + + return $return; + } +} + + +?> \ No newline at end of file diff --git a/3.0/modules/unrest/module.info b/3.0/modules/unrest/module.info new file mode 100644 index 00000000..aa1c5399 --- /dev/null +++ b/3.0/modules/unrest/module.info @@ -0,0 +1,3 @@ +name = "Un-Rest API module" +description = "Shorter invocations for the Rest API" +version = 1 From fe06c5d881995285a12ba53d3e074ba42b396a45 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Tue, 7 Dec 2010 23:22:48 +0100 Subject: [PATCH 115/300] WebDAV support for G3. --- 3.0/modules/webdav/controllers/webdav.php | 194 ++ 3.0/modules/webdav/helpers/webdav.php | 26 + 3.0/modules/webdav/helpers/webdav_event.php | 23 + .../webdav/libraries/Sabre.autoload.php | 16 + .../webdav/libraries/Sabre.includes.php | 127 ++ .../Sabre/CalDAV/Backend/Abstract.php | 151 ++ .../libraries/Sabre/CalDAV/Backend/PDO.php | 356 ++++ .../libraries/Sabre/CalDAV/Calendar.php | 254 +++ .../libraries/Sabre/CalDAV/CalendarObject.php | 176 ++ .../Sabre/CalDAV/CalendarRootNode.php | 74 + .../Exception/InvalidICalendarObject.php | 18 + .../libraries/Sabre/CalDAV/ICalendarUtil.php | 154 ++ .../webdav/libraries/Sabre/CalDAV/Plugin.php | 707 +++++++ .../SupportedCalendarComponentSet.php | 85 + .../CalDAV/Property/SupportedCalendarData.php | 38 + .../CalDAV/Property/SupportedCollationSet.php | 34 + .../webdav/libraries/Sabre/CalDAV/Server.php | 50 + .../libraries/Sabre/CalDAV/UserCalendars.php | 193 ++ .../webdav/libraries/Sabre/CalDAV/Version.php | 24 + .../webdav/libraries/Sabre/CalDAV/XMLUtil.php | 208 ++ .../Sabre/DAV/Auth/Backend/Abstract.php | 49 + .../Sabre/DAV/Auth/Backend/AbstractBasic.php | 85 + .../Sabre/DAV/Auth/Backend/AbstractDigest.php | 105 + .../Sabre/DAV/Auth/Backend/Apache.php | 77 + .../libraries/Sabre/DAV/Auth/Backend/File.php | 102 + .../libraries/Sabre/DAV/Auth/Backend/PDO.php | 79 + .../libraries/Sabre/DAV/Auth/Plugin.php | 434 ++++ .../libraries/Sabre/DAV/Auth/Principal.php | 147 ++ .../Sabre/DAV/Auth/PrincipalCollection.php | 74 + .../Sabre/DAV/Browser/GuessContentType.php | 97 + .../Sabre/DAV/Browser/MapGetToPropFind.php | 54 + .../libraries/Sabre/DAV/Browser/Plugin.php | 248 +++ .../webdav/libraries/Sabre/DAV/Directory.php | 90 + .../webdav/libraries/Sabre/DAV/Exception.php | 63 + .../Sabre/DAV/Exception/BadRequest.php | 28 + .../Sabre/DAV/Exception/Conflict.php | 28 + .../Sabre/DAV/Exception/ConflictingLock.php | 35 + .../Sabre/DAV/Exception/FileNotFound.php | 28 + .../Sabre/DAV/Exception/Forbidden.php | 27 + .../DAV/Exception/InsufficientStorage.php | 27 + .../DAV/Exception/InvalidResourceType.php | 33 + .../Exception/LockTokenMatchesRequestUri.php | 39 + .../libraries/Sabre/DAV/Exception/Locked.php | 67 + .../Sabre/DAV/Exception/MethodNotAllowed.php | 44 + .../Sabre/DAV/Exception/NotAuthenticated.php | 28 + .../Sabre/DAV/Exception/NotImplemented.php | 27 + .../DAV/Exception/PreconditionFailed.php | 69 + .../DAV/Exception/ReportNotImplemented.php | 30 + .../RequestedRangeNotSatisfiable.php | 29 + .../DAV/Exception/UnsupportedMediaType.php | 28 + .../libraries/Sabre/DAV/FS/Directory.php | 121 ++ .../webdav/libraries/Sabre/DAV/FS/File.php | 89 + .../webdav/libraries/Sabre/DAV/FS/Node.php | 81 + .../libraries/Sabre/DAV/FSExt/Directory.php | 135 ++ .../webdav/libraries/Sabre/DAV/FSExt/File.php | 88 + .../webdav/libraries/Sabre/DAV/FSExt/Node.php | 276 +++ .../webdav/libraries/Sabre/DAV/File.php | 81 + .../libraries/Sabre/DAV/ICollection.php | 58 + .../Sabre/DAV/IExtendedCollection.php | 28 + .../webdav/libraries/Sabre/DAV/IFile.php | 63 + .../webdav/libraries/Sabre/DAV/ILockable.php | 38 + .../webdav/libraries/Sabre/DAV/INode.php | 44 + .../libraries/Sabre/DAV/IProperties.php | 67 + .../webdav/libraries/Sabre/DAV/IQuota.php | 27 + .../Sabre/DAV/Locks/Backend/Abstract.php | 46 + .../libraries/Sabre/DAV/Locks/Backend/FS.php | 180 ++ .../libraries/Sabre/DAV/Locks/Backend/PDO.php | 141 ++ .../libraries/Sabre/DAV/Locks/LockInfo.php | 81 + .../libraries/Sabre/DAV/Locks/Plugin.php | 653 ++++++ .../libraries/Sabre/DAV/Mount/Plugin.php | 79 + .../webdav/libraries/Sabre/DAV/Node.php | 55 + .../webdav/libraries/Sabre/DAV/ObjectTree.php | 149 ++ .../webdav/libraries/Sabre/DAV/Property.php | 25 + .../Sabre/DAV/Property/GetLastModified.php | 75 + .../libraries/Sabre/DAV/Property/Href.php | 91 + .../libraries/Sabre/DAV/Property/IHref.php | 25 + .../Sabre/DAV/Property/LockDiscovery.php | 85 + .../Sabre/DAV/Property/Principal.php | 126 ++ .../Sabre/DAV/Property/ResourceType.php | 80 + .../libraries/Sabre/DAV/Property/Response.php | 156 ++ .../Sabre/DAV/Property/SupportedLock.php | 76 + .../Sabre/DAV/Property/SupportedReportSet.php | 110 + .../webdav/libraries/Sabre/DAV/Server.php | 1821 +++++++++++++++++ .../libraries/Sabre/DAV/ServerPlugin.php | 60 + .../libraries/Sabre/DAV/SimpleDirectory.php | 106 + .../Sabre/DAV/TemporaryFileFilterPlugin.php | 275 +++ .../webdav/libraries/Sabre/DAV/Tree.php | 192 ++ .../libraries/Sabre/DAV/Tree/Filesystem.php | 124 ++ .../webdav/libraries/Sabre/DAV/URLUtil.php | 141 ++ .../webdav/libraries/Sabre/DAV/Version.php | 24 + .../webdav/libraries/Sabre/DAV/XMLUtil.php | 160 ++ .../webdav/libraries/Sabre/HTTP/AWSAuth.php | 226 ++ .../libraries/Sabre/HTTP/AbstractAuth.php | 111 + .../webdav/libraries/Sabre/HTTP/BasicAuth.php | 61 + .../libraries/Sabre/HTTP/DigestAuth.php | 234 +++ .../webdav/libraries/Sabre/HTTP/Request.php | 220 ++ .../webdav/libraries/Sabre/HTTP/Response.php | 151 ++ .../webdav/libraries/Sabre/HTTP/Util.php | 65 + .../webdav/libraries/Sabre/HTTP/Version.php | 24 + .../webdav/libraries/Sabre/autoload.php | 27 + 3.0/modules/webdav/models/author_record.php | 21 + 3.0/modules/webdav/module.info | 3 + .../webdav/views/author_block.html.php | 6 + 103 files changed, 12660 insertions(+) create mode 100644 3.0/modules/webdav/controllers/webdav.php create mode 100644 3.0/modules/webdav/helpers/webdav.php create mode 100644 3.0/modules/webdav/helpers/webdav_event.php create mode 100755 3.0/modules/webdav/libraries/Sabre.autoload.php create mode 100755 3.0/modules/webdav/libraries/Sabre.includes.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/Abstract.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/ICalendarUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/XMLUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractBasic.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Directory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/BadRequest.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Conflict.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/ConflictingLock.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Forbidden.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/InsufficientStorage.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/InvalidResourceType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotImplemented.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/PreconditionFailed.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FS/Directory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ICollection.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IExtendedCollection.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IFile.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ILockable.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/INode.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IProperties.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/Abstract.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/FS.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Node.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ObjectTree.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/GetLastModified.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/LockDiscovery.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Server.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/SimpleDirectory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Tree.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Version.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/XMLUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Request.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Response.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Util.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Version.php create mode 100755 3.0/modules/webdav/libraries/Sabre/autoload.php create mode 100644 3.0/modules/webdav/models/author_record.php create mode 100644 3.0/modules/webdav/module.info create mode 100644 3.0/modules/webdav/views/author_block.html.php diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php new file mode 100644 index 00000000..6d2c443a --- /dev/null +++ b/3.0/modules/webdav/controllers/webdav.php @@ -0,0 +1,194 @@ +setBaseUri(url::site('webdav/gallery')); + $server->addPlugin($lock); + $server->addPlugin($filter); + + $this->doAuthenticate(); + $server->exec(); + } + + private function doAuthenticate() { + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setRealm('Gallery3'); + $authResult = $auth->getUserPass(); + list($username, $password) = $authResult; + + if ($username == '' || $password == '') { + $auth->requireLogin(); + die; + } + + $user = identity::lookup_user_by_name($username); + if (empty($user) || !identity::is_correct_password($user, $password)) { + $auth->requireLogin(); + die; + } + + identity::set_active_user($user); + return $user; + } +} + + +class Gallery3Album extends Sabre_DAV_Directory { + private $item; + private $stat; + + function __construct($name) { + $this->item = ORM::factory("item") + ->where("relative_path_cache", "=", rawurlencode($name)) + ->find(); + } + + function getName() { + return $this->item->name; + } + + function getChild($name) { + $rp = $this->item->relative_path() . '/' . rawurlencode($name); + if (substr($rp,0,1) == '/') { $rp = substr($rp, 1); } + + $child = ORM::factory("item") + ->where("relative_path_cache", "=", $rp) + ->find(); + + if (! access::can('view', $child)) { + return false; + }; + + if ($child->type == 'album') { + return new Gallery3Album($this->item->relative_path() . $child->name); + } else { + return new Gallery3File($rp); + } + } + + public function createFile($name, $data = null) { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (substr($name, 0, 1) == '.') { return true; }; + + $tempfile = tempnam(TMPPATH, 'dav'); + $target = fopen($tempfile, 'wb'); + stream_copy_to_stream($data, $target); + fclose($target); + + $parent_id = $this->item->__get('id'); + $item = ORM::factory("item"); + $item->name = $name; + $item->title = item::convert_filename_to_title($item->name); + $item->description = ''; + $item->parent_id = $parent_id; + $item->set_data_file($tempfile); + $item->type = "photo"; + $item->save(); + } + + public function createDirectory($name) { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + + $parent_id = $this->item->__get('id'); + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $parent_id; + $album->name = $name; + $album->title = $name; + $album->description = ''; + $album->save(); + + $this->item = ORM::factory("item")->where('id', '=', $parent_id); + } + + function setName($name) { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + + $this->item->name = $name; + $this->item->save(); + } + + public function delete() { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + $this->item->delete(); + } + + function getChildren() { + $return = array(); + foreach ($this->item->children() as $child) { + $item = $this->getChild($child->name); + if ($item != false) { + $return[] = $item; + } + } + return $return; + } +} + +class Gallery3File extends Sabre_DAV_File { + private $item; + private $stat; + private $path; + + function __construct($path) { + $this->item = ORM::factory("item") + ->where("relative_path_cache", "=", $path) + ->find(); + + if (access::can('view_full', $this->item)) { + $this->stat = stat($this->item->file_path()); + $this->path = $this->item->file_path(); + } else { + $this->stat = stat($this->item->resize_path()); + $this->path = $this->item->resize_path(); + } + } + + public function delete() { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + $this->item->delete(); + } + + function setName($name) { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + $this->item->name = $name; + $this->item->save(); + } + + public function getLastModified() { + return $this->stat[9]; + } + + function get() { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + return fopen($this->path,'r'); + } + + function getSize() { + return $this->stat[7]; + } + + function getName() { + return $this->item->name; + } + + function getETag() { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + return '"' . md5($this->item->file_path()) . '"'; + } +} + +?> \ No newline at end of file diff --git a/3.0/modules/webdav/helpers/webdav.php b/3.0/modules/webdav/helpers/webdav.php new file mode 100644 index 00000000..fa7c3fb2 --- /dev/null +++ b/3.0/modules/webdav/helpers/webdav.php @@ -0,0 +1,26 @@ + array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $properties + * @return bool|array + */ + public function updateCalendar($calendarId, array $properties) { + + return false; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + abstract function deleteCalendar($calendarId); + + /** + * Returns all calendar objects within a calendar object. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calnedar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * + * @param string $calendarId + * @return array + */ + abstract function getCalendarObjects($calendarId); + + /** + * Returns information from a single calendar object, based on it's object uri. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + abstract function getCalendarObject($calendarId,$objectUri); + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + abstract function createCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + abstract function updateCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + abstract function deleteCalendarObject($calendarId,$objectUri); + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php new file mode 100755 index 00000000..f60d1737 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php @@ -0,0 +1,356 @@ + 'displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + ); + + /** + * Creates the backend + * + * @param PDO $pdo + */ + public function __construct(PDO $pdo) { + + $this->pdo = $pdo; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principalUri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + public function getCalendarsForUser($principalUri) { + + $fields = array_values($this->propertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'ctag'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + + // Making fields a comma-delimited list + $fields = implode(', ', $fields); + $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM calendars WHERE principaluri = ?"); + $stmt->execute(array($principalUri)); + + $calendars = array(); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + + $components = explode(',',$row['components']); + + $calendar = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components), + ); + + + foreach($this->propertyMap as $xmlName=>$dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + $calendars[] = $calendar; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return mixed + */ + public function createCalendar($principalUri,$calendarUri, array $properties) { + + $fieldNames = array( + 'principaluri', + 'uri', + 'ctag', + ); + $values = array( + ':principaluri' => $principalUri, + ':uri' => $calendarUri, + ':ctag' => 1, + ); + + // Default value + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $fieldNames[] = 'components'; + if (!isset($properties[$sccs])) { + $values[':components'] = 'VEVENT,VTODO'; + } else { + if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) { + throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet'); + } + $values[':components'] = implode(',',$properties[$sccs]->getValue()); + } + + foreach($this->propertyMap as $xmlName=>$dbName) { + if (isset($properties[$xmlName])) { + + $myValue = $properties[$xmlName]; + $values[':' . $dbName] = $properties[$xmlName]; + $fieldNames[] = $dbName; + } + } + + $stmt = $this->pdo->prepare("INSERT INTO calendars (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); + $stmt->execute($values); + + return $this->pdo->lastInsertId(); + + } + + /** + * Updates a calendars properties + * + * The properties array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existant property is always succesful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $properties + * @return bool|array + */ + public function updateCalendar($calendarId, array $properties) { + + $newValues = array(); + $result = array( + 200 => array(), // Ok + 403 => array(), // Forbidden + 424 => array(), // Failed Dependency + ); + + $hasError = false; + + foreach($properties as $propertyName=>$propertyValue) { + + // We don't know about this property. + if (!isset($this->propertyMap[$propertyName])) { + $hasError = true; + $result[403][$propertyName] = null; + unset($properties[$propertyName]); + continue; + } + + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + + } + + // If there were any errors we need to fail the request + if ($hasError) { + // Properties has the remaining properties + foreach($properties as $propertyName=>$propertyValue) { + $result[424][$propertyName] = null; + } + + // Removing unused statuscodes for cleanliness + foreach($result as $status=>$properties) { + if (is_array($properties) && count($properties)===0) unset($result[$status]); + } + + return $result; + + } + + // Success + + // Now we're generating the sql query. + $valuesSql = array(); + foreach($newValues as $fieldName=>$value) { + $valuesSql[] = $fieldName . ' = ?'; + } + $valuesSql[] = 'ctag = ctag + 1'; + + $stmt = $this->pdo->prepare("UPDATE calendars SET " . implode(', ',$valuesSql) . " WHERE id = ?"); + $newValues['id'] = $calendarId; + $stmt->execute(array_values($newValues)); + + return true; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + public function deleteCalendar($calendarId) { + + $stmt = $this->pdo->prepare('DELETE FROM calendarobjects WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + + $stmt = $this->pdo->prepare('DELETE FROM calendars WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Returns all calendar objects within a calendar object. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calnedar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * + * @param string $calendarId + * @return array + */ + public function getCalendarObjects($calendarId) { + + $stmt = $this->pdo->prepare('SELECT * FROM calendarobjects WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + return $stmt->fetchAll(); + + } + + /** + * Returns information from a single calendar object, based on it's object uri. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + public function getCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('SELECT * FROM calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId, $objectUri)); + return $stmt->fetch(); + + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function createCalendarObject($calendarId,$objectUri,$calendarData) { + + $stmt = $this->pdo->prepare('INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)'); + $stmt->execute(array($calendarId,$objectUri,$calendarData,time())); + $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function updateCalendarObject($calendarId,$objectUri,$calendarData) { + + $stmt = $this->pdo->prepare('UPDATE calendarobjects SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarData,time(),$calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + public function deleteCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php new file mode 100755 index 00000000..b99878b7 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php @@ -0,0 +1,254 @@ +caldavBackend = $caldavBackend; + $this->authBackend = $authBackend; + $this->calendarInfo = $calendarInfo; + + + } + + /** + * Returns the name of the calendar + * + * @return string + */ + public function getName() { + + return $this->calendarInfo['uri']; + + } + + /** + * Updates properties such as the display name and description + * + * @param array $mutations + * @return array + */ + public function updateProperties($mutations) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations); + + } + + /** + * Returns the list of properties + * + * @param array $properties + * @return array + */ + public function getProperties($requestedProperties) { + + $response = array(); + + if (!$this->hasPrivilege()) return array(); + + foreach($requestedProperties as $prop) switch($prop) { + + case '{DAV:}resourcetype' : + $response[$prop] = new Sabre_DAV_Property_ResourceType(array('{urn:ietf:params:xml:ns:caldav}calendar','{DAV:}collection')); + break; + case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); + break; + case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet(); + break; + case '{DAV:}owner' : + $response[$prop] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::HREF,$this->calendarInfo['principaluri']); + break; + default : + if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; + break; + + } + return $response; + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return Sabre_DAV_ICalendarObject + */ + public function getChild($name) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Calendar object not found'); + return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + public function getChildren() { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + } + return $children; + + } + + /** + * Checks if a child-node exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) + return false; + else + return true; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in calendars. + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid ICalendar string. + * + * @param string $name + * @param resource $calendarData + * @return void + */ + public function createFile($name,$calendarData = null) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $calendarData = stream_get_contents($calendarData); + + $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue(); + Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); + + $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); + + } + + /** + * Deletes the calendar. + * + * @return void + */ + public function delete() { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); + + } + + /** + * Renames the calendar. Note that most calendars use the + * {DAV:}displayname to display a name to display a name. + * + * @param string $newName + * @return void + */ + public function setName($newName) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return void + */ + public function getLastModified() { + + return null; + + } + + /** + * Check if user has access. + * + * This method does a check if the currently logged in user + * has permission to access this calendar. There is only read-write + * access, so you're in or you're out. + * + * @return bool + */ + protected function hasPrivilege() { + + if (!$user = $this->authBackend->getCurrentUser()) return false; + if ($user['uri']!==$this->calendarInfo['principaluri']) return false; + return true; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php new file mode 100755 index 00000000..937bab19 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php @@ -0,0 +1,176 @@ +caldavBackend = $caldavBackend; + $this->calendarInfo = $calendarInfo; + $this->objectData = $objectData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + public function getName() { + + return $this->objectData['uri']; + + } + + /** + * Returns the ICalendar-formatted object + * + * @return string + */ + public function get() { + + return $this->objectData['calendardata']; + + } + + /** + * Updates the ICalendar-formatted object + * + * @param string $calendarData + * @return void + */ + public function put($calendarData) { + + if (is_resource($calendarData)) + $calendarData = stream_get_contents($calendarData); + + $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue(); + Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); + + $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); + $this->objectData['calendardata'] = $calendarData; + + } + + /** + * Deletes the calendar object + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + public function getContentType() { + + return 'text/calendar'; + + } + + /** + * Returns an ETag for this object. + * + * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * + * @return string + */ + public function getETag() { + + return '"' . md5($this->objectData['calendardata']). '"'; + + } + + /** + * Returns the list of properties for this object + * + * @param array $properties + * @return array + */ + public function getProperties($properties) { + + $response = array(); + if (in_array('{urn:ietf:params:xml:ns:caldav}calendar-data',$properties)) + $response['{urn:ietf:params:xml:ns:caldav}calendar-data'] = str_replace("\r","",$this->objectData['calendardata']); + + + return $response; + + } + + /** + * Updates properties + * + * @param array $properties + * @return array + */ + public function updateProperties($properties) { + + return false; + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return time + */ + public function getLastModified() { + + return strtotime($this->objectData['lastmodified']); + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + public function getSize() { + + return strlen($this->objectData['calendardata']); + + } +} + diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php new file mode 100755 index 00000000..322722a1 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php @@ -0,0 +1,74 @@ +authBackend = $authBackend; + $this->caldavBackend = $caldavBackend; + + } + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + + return Sabre_CalDAV_Plugin::CALENDAR_ROOT; + + } + + /** + * Returns the list of users as Sabre_CalDAV_User objects. + * + * @return array + */ + public function getChildren() { + + $users = $this->authBackend->getUsers(); + $children = array(); + foreach($users as $user) { + + $children[] = new Sabre_CalDAV_UserCalendars($this->authBackend, $this->caldavBackend, $user['uri']); + + } + return $children; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php new file mode 100755 index 00000000..24873f5d --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php @@ -0,0 +1,18 @@ +registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal'); + + // Check if there's only 1 component + $components = array('vevent','vtodo','vjournal','vfreebusy'); + $componentsFound = array(); + + foreach($components as $component) { + $test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component); + if (is_array($test)) $componentsFound = array_merge($componentsFound, $test); + } + if (count($componentsFound)>1) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Only 1 of VEVENT, VTODO, VJOURNAL or VFREEBUSY may be specified per calendar object'); + } + if (count($componentsFound)<1) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.'); + } + $component = $componentsFound[0]; + + // Check if the component is allowed + $name = $component->getName(); + if (!in_array(strtoupper($name),$allowedComponents)) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.'); + } + + if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects'); + } + + return true; + + } + + /** + * Converts ICalendar data to XML. + * + * Properties are converted to lowercase xml elements. Parameters are; + * converted to attributes. BEGIN:VEVENT is converted to and + * END:VEVENT as well as other components. + * + * It's a very loose parser. If any line does not conform to the spec, it + * will simply be ignored. It will try to detect if \r\n or \n line endings + * are used. + * + * @todo Currently quoted attributes are not parsed correctly. + * @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03 + * @param string $icalData + * @return string. + */ + static function toXCAL($icalData) { + + // Detecting line endings + $lb="\r\n"; + if (strpos($icalData,"\r\n")!==false) $lb = "\r\n"; + elseif (strpos($icalData,"\n")!==false) $lb = "\n"; + + // Splitting up items per line + $lines = explode($lb,$icalData); + + // Properties can be folded over 2 lines. In this case the second + // line will be preceeded by a space or tab. + $lines2 = array(); + foreach($lines as $line) { + + if (!$line) continue; + if ($line[0]===" " || $line[0]==="\t") { + $lines2[count($lines2)-1].=substr($line,1); + continue; + } + + $lines2[]=$line; + + } + + $xml = '' . "\n"; + $xml.= "\n"; + + $spaces = 2; + foreach($lines2 as $line) { + + $matches = array(); + // This matches PROPERTYNAME;ATTRIBUTES:VALUE + if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches)) + continue; + + $propertyName = strtolower($matches[1]); + $attributes = $matches[2]; + $value = $matches[3]; + + // If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it. + if ($propertyName === 'begin') { + $xml.=str_repeat(" ",$spaces); + $xml.='<' . strtolower($value) . ">\n"; + $spaces+=2; + continue; + } elseif ($propertyName === 'end') { + $spaces-=2; + $xml.=str_repeat(" ",$spaces); + $xml.='\n"; + continue; + } + + $xml.=str_repeat(" ",$spaces); + $xml.='<' . $propertyName; + if ($attributes) { + // There can be multiple attributes + $attributes = explode(';',$attributes); + foreach($attributes as $att) { + + list($attName,$attValue) = explode('=',$att,2); + $attName = strtolower($attName); + if ($attName === 'language') $attName='xml:lang'; + $xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"'; + + } + } + + $xml.='>'. htmlspecialchars(trim($value)) . '\n"; + + } + $xml.=""; + return $xml; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php new file mode 100755 index 00000000..7a29891b --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php @@ -0,0 +1,707 @@ +server->tree->getNodeForPath($parent); + + if ($node instanceof Sabre_DAV_IExtendedCollection) { + try { + $node->getChild($name); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + return array('MKCALENDAR'); + } + } + return array(); + + } + + /** + * Returns a list of features for the DAV: HTTP header. + * + * @return array + */ + public function getFeatures() { + + return array('calendar-access'); + + } + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); + $server->subscribeEvent('report',array($this,'report')); + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + + $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; + $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; + + $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet'; + + array_push($server->protectedProperties, + + '{' . self::NS_CALDAV . '}supported-calendar-component-set', + '{' . self::NS_CALDAV . '}supported-calendar-data', + '{' . self::NS_CALDAV . '}max-resource-size', + '{' . self::NS_CALDAV . '}min-date-time', + '{' . self::NS_CALDAV . '}max-date-time', + '{' . self::NS_CALDAV . '}max-instances', + '{' . self::NS_CALDAV . '}max-attendees-per-instance', + '{' . self::NS_CALDAV . '}calendar-home-set', + '{' . self::NS_CALDAV . '}supported-collation-set', + + // scheduling extension + '{' . self::NS_CALDAV . '}calendar-user-address-set' + + ); + } + + /** + * This function handles support for the MKCALENDAR method + * + * @param string $method + * @return bool + */ + public function unknownMethod($method, $uri) { + + if ($method!=='MKCALENDAR') return; + + $this->httpMkCalendar($uri); + // false is returned to stop the unknownMethod event + return false; + + } + + /** + * This functions handles REPORT requests specific to CalDAV + * + * @param string $reportName + * @param DOMNode $dom + * @return bool + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{'.self::NS_CALDAV.'}calendar-multiget' : + $this->calendarMultiGetReport($dom); + return false; + case '{'.self::NS_CALDAV.'}calendar-query' : + $this->calendarQueryReport($dom); + return false; + default : + return true; + + } + + + } + + /** + * This function handles the MKCALENDAR HTTP method, which creates + * a new calendar. + * + * @param string $uri + * @return void + */ + public function httpMkCalendar($uri) { + + // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support + // for clients matching iCal in the user agent + //$ua = $this->server->httpRequest->getHeader('User-Agent'); + //if (strpos($ua,'iCal/')!==false) { + // throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); + //} + + $body = $this->server->httpRequest->getBody(true); + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $properties = array(); + foreach($dom->firstChild->childNodes as $child) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; + foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { + $properties[$k] = $prop; + } + + } + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + + $this->server->createCollection($uri,$resourceType,$properties); + + $this->server->httpResponse->sendStatus(201); + $this->server->httpResponse->setHeader('Content-Length',0); + } + + /** + * afterGetProperties + * + * This method handler is invoked after properties for a specific resource + * are received. This allows us to add any properties that might have been + * missing. + * + * @param string $path + * @param array $properties + * @return void + */ + public function afterGetProperties($path, &$properties) { + + // Find out if we are currently looking at a principal resource + $currentNode = $this->server->tree->getNodeForPath($path); + if ($currentNode instanceof Sabre_DAV_Auth_Principal) { + + // calendar-home-set property + $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; + if (array_key_exists($calHome,$properties[404])) { + $principalId = $currentNode->getName(); + $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; + unset($properties[404][$calHome]); + $properties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath); + } + + // calendar-user-address-set property + $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; + if (array_key_exists($calProp,$properties[404])) { + + // Do we have an email address? + $props = $currentNode->getProperties(array('{http://sabredav.org/ns}email-address')); + if (isset($props['{http://sabredav.org/ns}email-address'])) { + $email = $props['{http://sabredav.org/ns}email-address']; + } else { + // We're going to make up an emailaddress + $email = $currentNode->getName() . '.sabredav@' . $this->server->httpRequest->getHeader('host'); + } + $properties[200][$calProp] = new Sabre_DAV_Property_Href('mailto:' . $email, false); + unset($properties[404][$calProp]); + + } + + + } + + if ($currentNode instanceof Sabre_CalDAV_Calendar || $currentNode instanceof Sabre_CalDAV_CalendarObject) { + if (array_key_exists('{DAV:}supported-report-set', $properties[200])) { + $properties[200]['{DAV:}supported-report-set']->addReport(array( + '{' . self::NS_CALDAV . '}calendar-multiget', + '{' . self::NS_CALDAV . '}calendar-query', + // '{' . self::NS_CALDAV . '}supported-collation-set', + // '{' . self::NS_CALDAV . '}free-busy-query', + )); + } + } + + + } + + /** + * This function handles the calendar-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param DOMNode $dom + * @return void + */ + public function calendarMultiGetReport($dom) { + + $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + + $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + foreach($hrefElems as $elem) { + $uri = $this->server->calculateUri($elem->nodeValue); + list($objProps) = $this->server->getPropertiesForPath($uri,$properties); + $propertyList[]=$objProps; + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); + + } + + /** + * This function handles the calendar-query REPORT + * + * This report is used by clients to request calendar objects based on + * complex conditions. + * + * @param DOMNode $dom + * @return void + */ + public function calendarQueryReport($dom) { + + $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + + $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter'); + if ($filterNode->length!==1) { + throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element'); + } + $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0)); + + $requestedCalendarData = true; + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } + + // These are the list of nodes that potentially match the requirement + $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(),$requestedProperties,$this->server->getHTTPDepth(0)); + + $verifiedNodes = array(); + + foreach($candidateNodes as $node) { + + // If the node didn't have a calendar-data property, it must not be a calendar object + if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) continue; + + if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'],$filters)) { + + if (!$requestedCalendarData) { + unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + } + $verifiedNodes[] = $node; + } + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); + + } + + + /** + * Verify if a list of filters applies to the calendar data object + * + * The calendarData object must be a valid iCalendar blob. The list of + * filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters + * + * @param string $calendarData + * @param array $filters + * @return bool + */ + public function validateFilters($calendarData,$filters) { + + // We are converting the calendar object to an XML structure + // This makes it far easier to parse + $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData); + $xml = simplexml_load_string($xCalendarData); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal'); + + foreach($filters as $xpath=>$filter) { + + // if-not-defined comes first + if (isset($filter['is-not-defined'])) { + if (!$xml->xpath($xpath)) + continue; + else + return false; + + } + + $elem = $xml->xpath($xpath); + + if (!$elem) return false; + $elem = $elem[0]; + + if (isset($filter['time-range'])) { + + switch($elem->getName()) { + case 'vevent' : + $result = $this->validateTimeRangeFilterForEvent($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'vtodo' : + $result = $this->validateTimeRangeFilterForTodo($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'vjournal' : + // TODO: not implemented + break; + $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'vfreebusy' : + // TODO: not implemented + break; + $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'valarm' : + // TODO: not implemented + break; + $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter); + if ($result===false) return false; + break; + + } + + } + + if (isset($filter['text-match'])) { + $currentString = (string)$elem; + + $isMatching = $this->substringMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']); + if ($filter['text-match']['negate-condition'] && $isMatching) return false; + if (!$filter['text-match']['negate-condition'] && !$isMatching) return false; + + } + + } + return true; + + } + + private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) { + + // Grabbing the DTSTART property + $xdtstart = $xml->xpath($currentXPath.'/c:dtstart'); + if (!count($xdtstart)) { + throw new Sabre_DAV_Exception_BadRequest('DTSTART property missing from calendar object'); + } + + // The dtstart can be both a date, or datetime property + if ((string)$xdtstart[0]['value']==='DATE') { + $isDateTime = false; + } else { + $isDateTime = true; + } + + // Determining the timezone + if ($tzid = (string)$xdtstart[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + if ($isDateTime) { + $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtstart[0],$tz); + } else { + $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtstart[0]); + } + + + // Grabbing the DTEND property + $xdtend = $xml->xpath($currentXPath.'/c:dtend'); + $dtend = null; + + if (count($xdtend)) { + // Determining the timezone + if ($tzid = (string)$xdtend[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + + // Since the VALUE parameter of both DTSTART and DTEND must be the same + // we can assume we don't need to check the VALUE paramter of DTEND. + if ($isDateTime) { + $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz); + } else { + $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtend[0],$tz); + } + + } + + if (is_null($dtend)) { + // The DTEND property was not found. We will first see if the event has a duration + // property + + $xduration = $xml->xpath($currentXPath.'/c:duration'); + if (count($xduration)) { + $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]); + + // Making sure that the duration is bigger than 0 seconds. + $tempDT = clone $dtstart; + $tempDT->modify($duration); + if ($tempDT > $dtstart) { + + // use DTEND = DTSTART + DURATION + $dtend = $tempDT; + } else { + // use DTEND = DTSTART + $dtend = $dtstart; + } + + } + } + + if (is_null($dtend)) { + if ($isDateTime) { + // DTEND = DTSTART + $dtend = $dtstart; + } else { + // DTEND = DTSTART + 1 DAY + $dtend = clone $dtstart; + $dtend->modify('+1 day'); + } + } + + if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend) return false; + if (!is_null($currentFilter['time-range']['end']) && $currentFilter['time-range']['end'] <= $dtstart) return false; + return true; + + } + + private function validateTimeRangeFilterForTodo(SimpleXMLElement $xml,$currentXPath,array $filter) { + + // Gathering all relevant elements + + $dtStart = null; + $duration = null; + $due = null; + $completed = null; + $created = null; + + $xdt = $xml->xpath($currentXPath.'/c:dtstart'); + if (count($xdt)) { + // The dtstart can be both a date, or datetime property + if ((string)$xdt[0]['value']==='DATE') { + $isDateTime = false; + } else { + $isDateTime = true; + } + + // Determining the timezone + if ($tzid = (string)$xdt[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + if ($isDateTime) { + $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz); + } else { + $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]); + } + } + + // Only need to grab duration if dtStart is set + if (!is_null($dtStart)) { + + $xduration = $xml->xpath($currentXPath.'/c:duration'); + if (count($xduration)) { + $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]); + } + + } + + if (!is_null($dtStart) && !is_null($duration)) { + + // Comparision from RFC 4791: + // (start <= DTSTART+DURATION) AND ((end > DTSTART) OR (end >= DTSTART+DURATION)) + + $end = clone $dtStart; + $end->modify($duration); + + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $end) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart || $filter['time-range']['end'] >= $end) ) { + return true; + } else { + return false; + } + + } + + // Need to grab the DUE property + $xdt = $xml->xpath($currentXPath.'/c:due'); + if (count($xdt)) { + // The due property can be both a date, or datetime property + if ((string)$xdt[0]['value']==='DATE') { + $isDateTime = false; + } else { + $isDateTime = true; + } + // Determining the timezone + if ($tzid = (string)$xdt[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + if ($isDateTime) { + $due = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz); + } else { + $due = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]); + } + } + + if (!is_null($dtStart) && !is_null($due)) { + + // Comparision from RFC 4791: + // ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE)) + + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due || $filter['time-range']['start'] < $dtstart) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) { + return true; + } else { + return false; + } + + } + + if (!is_null($dtStart)) { + + // Comparision from RFC 4791 + // (start <= DTSTART) AND (end > DTSTART) + if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $dtStart) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart) ) { + return true; + } else { + return false; + } + + } + + if (!is_null($due)) { + + // Comparison from RFC 4791 + // (start < DUE) AND (end >= DUE) + if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) { + return true; + } else { + return false; + } + + } + // Need to grab the COMPLETED property + $xdt = $xml->xpath($currentXPath.'/c:completed'); + if (count($xdt)) { + $completed = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]); + } + // Need to grab the CREATED property + $xdt = $xml->xpath($currentXPath.'/c:created'); + if (count($xdt)) { + $created = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]); + } + + if (!is_null($completed) && !is_null($created)) { + // Comparison from RFC 4791 + // ((start <= CREATED) OR (start <= COMPLETED)) AND ((end >= CREATED) OR (end >= COMPLETED)) + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $created || $filter['time-range']['start'] <= $completed) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $created || $filter['time-range']['end'] >= $completed)) { + return true; + } else { + return false; + } + } + + if (!is_null($completed)) { + // Comparison from RFC 4791 + // (start <= COMPLETED) AND (end >= COMPLETED) + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $completed) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $completed)) { + return true; + } else { + return false; + } + } + + if (!is_null($created)) { + // Comparison from RFC 4791 + // (end > CREATED) + if( (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $created) ) { + return true; + } else { + return false; + } + } + + // Everything else is TRUE + return true; + + } + + public function substringMatch($haystack, $needle, $collation) { + + switch($collation) { + case 'i;ascii-casemap' : + // default strtolower takes locale into consideration + // we don't want this. + $haystack = str_replace(range('a','z'), range('A','Z'), $haystack); + $needle = str_replace(range('a','z'), range('A','Z'), $needle); + return strpos($haystack, $needle)!==false; + + case 'i;octet' : + return strpos($haystack, $needle)!==false; + + default: + throw new Sabre_DAV_Exception_BadRequest('Unknown collation: ' . $collation); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php new file mode 100755 index 00000000..5a1862d0 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php @@ -0,0 +1,85 @@ +components = $components; + + } + + /** + * Returns the list of supported components + * + * @return array + */ + public function getValue() { + + return $this->components; + + } + + /** + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + foreach($this->components as $component) { + + $xcomp = $doc->createElement('cal:comp'); + $xcomp->setAttribute('name',$component); + $node->appendChild($xcomp); + + } + + } + + /** + * Unserializes the DOMElement back into a Property class. + * + * @param DOMElement $node + * @return void + */ + static function unserialize(DOMElement $node) { + + $components = array(); + foreach($node->childNodes as $childNode) { + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') { + $components[] = $childNode->getAttribute('name'); + } + } + return new self($components); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php new file mode 100755 index 00000000..3e9a9d76 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php @@ -0,0 +1,38 @@ +ownerDocument; + + $prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal'; + + $caldata = $doc->createElement($prefix . ':calendar-data'); + $caldata->setAttribute('content-type','text/calendar'); + $caldata->setAttribute('version','2.0'); + + $node->appendChild($caldata); + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php new file mode 100755 index 00000000..2f96591b --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php @@ -0,0 +1,34 @@ +ownerDocument; + + $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); + if (!$prefix) $prefix = 'cal'; + + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;ascii-casemap') + ); + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;octet') + ); + + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php new file mode 100755 index 00000000..33f7a39c --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php @@ -0,0 +1,50 @@ +addChild($principals); + $calendars = new Sabre_CalDAV_CalendarRootNode($authBackend, $calendarBackend); + $root->addChild($calendars); + + $objectTree = new Sabre_DAV_ObjectTree($root); + + /* Initializing server */ + parent::__construct($objectTree); + + /* Server Plugins */ + $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV'); + $this->addPlugin($authPlugin); + + $caldavPlugin = new Sabre_CalDAV_Plugin(); + $this->addPlugin($caldavPlugin); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php new file mode 100755 index 00000000..c81b70ba --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php @@ -0,0 +1,193 @@ +authBackend = $authBackend; + $this->caldavBackend = $caldavBackend; + $this->userUri = $userUri; + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->userUri); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Deletes this object + * + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @todo needs optimizing + * @return Sabre_CalDAV_Calendar + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found'); + + } + + /** + * Checks if a calendar exists. + * + * @param string $name + * @todo needs optimizing + * @return bool + */ + public function childExists($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return true; + + } + return false; + + } + + /** + * Returns a list of calendars + * + * @return array + */ + public function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->userUri); + $objs = array(); + foreach($calendars as $calendar) { + $objs[] = new Sabre_CalDAV_Calendar($this->authBackend, $this->caldavBackend, $calendar); + } + return $objs; + + } + + /** + * Creates a new calendar + * + * @param string $name + * @param string $properties + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->caldavBackend->createCalendar($this->userUri, $name, $properties); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php new file mode 100755 index 00000000..d7b2e6af --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php @@ -0,0 +1,24 @@ +childNodes as $child) { + + switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) { + + case '{urn:ietf:params:xml:ns:caldav}comp-filter' : + case '{urn:ietf:params:xml:ns:caldav}prop-filter' : + + $filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name')); + $filters[$filterName] = array(); + + self::parseCalendarQueryFilters($child, $filterName,$filters); + break; + + case '{urn:ietf:params:xml:ns:caldav}time-range' : + + if ($start = $child->getAttribute('start')) { + $start = self::parseICalendarDateTime($start); + } else { + $start = null; + } + if ($end = $child->getAttribute('end')) { + $end = self::parseICalendarDateTime($end); + } else { + $end = null; + } + + if (!is_null($start) && !is_null($end) && $end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); + } + + $filters[$basePath]['time-range'] = array( + 'start' => $start, + 'end' => $end + ); + break; + + case '{urn:ietf:params:xml:ns:caldav}is-not-defined' : + $filters[$basePath]['is-not-defined'] = true; + break; + + case '{urn:ietf:params:xml:ns:caldav}param-filter' : + + $filterName = $basePath . '/@' . strtolower($child->getAttribute('name')); + $filters[$filterName] = array(); + self::parseCalendarQueryFilters($child, $filterName, $filters); + break; + + case '{urn:ietf:params:xml:ns:caldav}text-match' : + + $collation = $child->getAttribute('collation'); + if (!$collation) $collation = 'i;ascii-casemap'; + + $filters[$basePath]['text-match'] = array( + 'collation' => $collation, + 'negate-condition' => $child->getAttribute('negate-condition')==='yes', + 'value' => $child->nodeValue, + ); + break; + + } + + } + + return $filters; + + } + + /** + * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object + * + * Specifying a reference timezone is optional. It will only be used + * if the non-UTC format is used. The argument is used as a reference, the + * returned DateTime object will still be in the UTC timezone. + * + * @param string $dt + * @param DateTimeZone $tz + * @return DateTime + */ + static public function parseICalendarDateTime($dt,DateTimeZone $tz = null) { + + // Format is YYYYMMDD + "T" + hhmmss + $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); + + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt); + } + + if ($matches[7]==='Z' || is_null($tz)) { + $tz = new DateTimeZone('UTC'); + } + $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); + + // Still resetting the timezone, to normalize everything to UTC + $date->setTimeZone(new DateTimeZone('UTC')); + return $date; + + } + + /** + * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object + * + * @param string $date + * @param DateTimeZone $tz + * @return DateTime + */ + static public function parseICalendarDate($date) { + + // Format is YYYYMMDD + $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); + + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); + } + + $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); + return $date; + + } + + /** + * Parses an iCalendar (RFC5545) formatted duration and returns a string suitable + * for strtotime or DateTime::modify. + * + * NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which + * supports ISO 8601 Intervals, which is a superset of ICalendar durations. + * + * For now though, we're just gonna live with this messy system + * + * @param string $duration + * @return string + */ + static public function parseICalendarDuration($duration) { + + $result = preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $duration, $matches); + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration); + } + + $parts = array( + 'week', + 'day', + 'hour', + 'minute', + 'second', + ); + + $newDur = ''; + foreach($parts as $part) { + if (isset($matches[$part]) && $matches[$part]) { + $newDur.=' '.$matches[$part] . ' ' . $part . 's'; + } + } + + $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); + return $newDur; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php new file mode 100755 index 00000000..79383fa3 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php @@ -0,0 +1,49 @@ +currentUser; + } + + + /** + * Authenticates the user based on the current request. + * + * If authentication is succesful, true must be returned. + * If authentication fails, an exception must be thrown. + * + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function authenticate(Sabre_DAV_Server $server,$realm) { + + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setHTTPRequest($server->httpRequest); + $auth->setHTTPResponse($server->httpResponse); + $auth->setRealm($realm); + $userpass = $auth->getUserPass(); + if (!$userpass) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found'); + } + + // Authenticates the user + if (!($userData = $this->validateUserPass($userpass[0],$userpass[1]))) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match'); + } + if (!isset($userData['uri'])) { + throw new Sabre_DAV_Exception('The returned array from validateUserPass must contain at a uri element'); + } + $this->currentUser = $userData; + return true; + } + + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php new file mode 100755 index 00000000..d8cdcb18 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php @@ -0,0 +1,105 @@ +setHTTPRequest($server->httpRequest); + $digest->setHTTPResponse($server->httpResponse); + + $digest->setRealm($realm); + $digest->init(); + + $username = $digest->getUsername(); + + // No username was given + if (!$username) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found'); + } + + $userData = $this->getUserInfo($realm, $username); + // If this was false, the user account didn't exist + if ($userData===false) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file'); + } + if (!is_array($userData)) { + throw new Sabre_DAV_Exception('The returntype for getUserInfo must be either false or an array'); + } + + if (!isset($userData['uri']) || !isset($userData['digestHash'])) { + throw new Sabre_DAV_Exception('The returned array from getUserInfo must contain at least a uri and digestHash element'); + } + + // If this was false, the password or part of the hash was incorrect. + if (!$digest->validateA1($userData['digestHash'])) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username'); + } + + $this->currentUser = $userData; + return true; + + } + + /** + * Returns information about the currently logged in user. + * + * @return array|null + */ + public function getCurrentUser() { + + return $this->currentUser; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php new file mode 100755 index 00000000..0b46270f --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php @@ -0,0 +1,77 @@ +httpRequest->getRawServerValue('REMOTE_USER'); + if (is_null($remoteUser)) { + throw new Sabre_DAV_Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured'); + } + + $this->remoteUser = $remoteUser; + return true; + + } + + /** + * Returns information about the currently logged in user. + * + * If nobody is currently logged in, this method should return null. + * + * @return array|null + */ + public function getCurrentUser() { + + return array( + 'uri' => 'principals/' . $this->remoteUser, + ); + + } + + /** + * Returns the full list of users. + * + * This method must at least return a uri for each user. + * + * It is optional to implement this. + * + * @return array + */ + public function getUsers() { + + return array($this->getCurrentUser()); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php new file mode 100755 index 00000000..953c49e6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php @@ -0,0 +1,102 @@ +loadFile($filename); + + } + + /** + * Loads an htdigest-formatted file. This method can be called multiple times if + * more than 1 file is used. + * + * @param string $filename + * @return void + */ + public function loadFile($filename) { + + foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { + + if (substr_count($line, ":") !== 2) + throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons'); + + list($username,$realm,$A1) = explode(':',$line); + + if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) + throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash'); + + $this->users[$username] = array( + 'digestHash' => $A1, + 'uri' => 'principals/' . $username + ); + + } + + } + + /** + * Returns a users' information + * + * @param string $realm + * @param string $username + * @return string + */ + public function getUserInfo($realm, $username) { + + return isset($this->users[$username])?$this->users[$username]:false; + + } + + + /** + * Returns the full list of users. + * + * This method must at least return a uri for each user. + * + * @return array + */ + public function getUsers() { + + $re = array(); + foreach($this->users as $userName=>$A1) { + + $re[] = array( + 'uri'=>'principals/' . $userName + ); + + } + + return $re; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php new file mode 100755 index 00000000..c8808580 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php @@ -0,0 +1,79 @@ +pdo = $pdo; + + } + + /** + * Returns a users' information + * + * @param string $realm + * @param string $username + * @return string + */ + public function getUserInfo($realm,$username) { + + $stmt = $this->pdo->prepare('SELECT username, digesta1, email FROM users WHERE username = ?'); + $stmt->execute(array($username)); + $result = $stmt->fetchAll(); + + if (!count($result)) return false; + $user = array( + 'uri' => 'principals/' . $result[0]['username'], + 'digestHash' => $result[0]['digesta1'], + ); + if ($result[0]['email']) $user['{http://sabredav.org/ns}email-address'] = $result[0]['email']; + return $user; + + } + + /** + * Returns a list of all users + * + * @return array + */ + public function getUsers() { + + $result = $this->pdo->query('SELECT username, email FROM users')->fetchAll(); + + $rv = array(); + foreach($result as $user) { + + $r = array( + 'uri' => 'principals/' . $user['username'], + ); + if ($user['email']) $r['{http://sabredav.org/ns}email-address'] = $user['email']; + $rv[] = $r; + + } + + return $rv; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php new file mode 100755 index 00000000..cc4f25f3 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php @@ -0,0 +1,434 @@ +authBackend = $authBackend; + $this->realm = $realm; + + } + + /** + * Initializes the plugin. This function is automatically called by the server + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10); + $this->server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + $this->server->subscribeEvent('report',array($this,'report')); + + } + + /** + * This method intercepts calls to PROPFIND and similar lookups + * + * This is done to inject the current-user-principal if this is requested. + * + * @todo support for 'unauthenticated' + * @return void + */ + public function afterGetProperties($href, &$properties) { + + if (array_key_exists('{DAV:}current-user-principal', $properties[404])) { + if ($ui = $this->authBackend->getCurrentUser()) { + $properties[200]['{DAV:}current-user-principal'] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::HREF, $ui['uri']); + } else { + $properties[200]['{DAV:}current-user-principal'] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::UNAUTHENTICATED); + } + unset($properties[404]['{DAV:}current-user-principal']); + } + if (array_key_exists('{DAV:}principal-collection-set', $properties[404])) { + $properties[200]['{DAV:}principal-collection-set'] = new Sabre_DAV_Property_Href('principals'); + unset($properties[404]['{DAV:}principal-collection-set']); + } + if (array_key_exists('{DAV:}supported-report-set', $properties[200])) { + $properties[200]['{DAV:}supported-report-set']->addReport(array( + '{DAV:}expand-property', + )); + } + + + } + + /** + * This method is called before any HTTP method and forces users to be authenticated + * + * @param string $method + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function beforeMethod($method, $uri) { + + $this->authBackend->authenticate($this->server,$this->realm); + + } + + /** + * This functions handles REPORT requests + * + * @param string $reportName + * @param DOMNode $dom + * @return bool|null + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{DAV:}expand-property' : + $this->expandPropertyReport($dom); + return false; + case '{DAV:}principal-property-search' : + if ($this->server->getRequestUri()==='principals') { + $this->principalPropertySearchReport($dom); + return false; + } + break; + case '{DAV:}principal-search-property-set' : + if ($this->server->getRequestUri()==='principals') { + $this->principalSearchPropertySetReport($dom); + return false; + } + break; + + } + + } + + /** + * The expand-property report is defined in RFC3253 section 3-8. + * + * This report is very similar to a standard PROPFIND. The difference is + * that it has the additional ability to look at properties containing a + * {DAV:}href element, follow that property and grab additional elements + * there. + * + * Other rfc's, such as ACL rely on this report, so it made sense to put + * it in this plugin. + * + * @param DOMElement $dom + * @return void + */ + protected function expandPropertyReport($dom) { + + $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild); + $depth = $this->server->getHTTPDepth(0); + $requestUri = $this->server->getRequestUri(); + + $result = $this->expandProperties($requestUri,$requestedProperties,$depth); + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $multiStatus = $dom->createElement('d:multistatus'); + $dom->appendChild($multiStatus); + + // Adding in default namespaces + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); + + } + + foreach($result as $entry) { + + $entry->serialize($this->server,$multiStatus); + + } + + $xml = $dom->saveXML(); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->sendBody($xml); + + // Make sure the event chain is broken + return false; + + } + + /** + * This method is used by expandPropertyReport to parse + * out the entire HTTP request. + * + * @param DOMElement $node + * @return array + */ + protected function parseExpandPropertyReportRequest($node) { + + $requestedProperties = array(); + do { + + if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; + + if ($node->firstChild) { + + $children = $this->parseExpandPropertyReportRequest($node->firstChild); + + } else { + + $children = array(); + + } + + $namespace = $node->getAttribute('namespace'); + if (!$namespace) $namespace = 'DAV:'; + + $propName = '{'.$namespace.'}' . $node->getAttribute('name'); + $requestedProperties[$propName] = $children; + + } while ($node = $node->nextSibling); + + return $requestedProperties; + + } + + /** + * This method expands all the properties and returns + * a list with property values + * + * @param array $path + * @param array $requestedProperties the list of required properties + * @param array $depth + */ + protected function expandProperties($path,array $requestedProperties,$depth) { + + $foundProperties = $this->server->getPropertiesForPath($path,array_keys($requestedProperties),$depth); + + $result = array(); + + foreach($foundProperties as $node) { + + foreach($requestedProperties as $propertyName=>$childRequestedProperties) { + + // We're only traversing if sub-properties were requested + if(count($childRequestedProperties)===0) continue; + + // We only have to do the expansion if the property was found + // and it contains an href element. + if (!array_key_exists($propertyName,$node[200])) continue; + if (!($node[200][$propertyName] instanceof Sabre_DAV_Property_IHref)) continue; + + $href = $node[200][$propertyName]->getHref(); + list($node[200][$propertyName]) = $this->expandProperties($href,$childRequestedProperties,0); + + } + $result[] = new Sabre_DAV_Property_Response($path, $node); + + } + + return $result; + + } + + protected function principalSearchPropertySetReport(DOMDocument $dom) { + + $searchProperties = array( + '{DAV:}displayname' => 'display name' + + ); + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth!==0) { + throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); + } + + if ($dom->firstChild->hasChildNodes()) + throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $root = $dom->createElement('d:principal-search-property-set'); + $dom->appendChild($root); + // Adding in default namespaces + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $root->setAttribute('xmlns:' . $prefix,$namespace); + + } + + $nsList = $this->server->xmlNamespaces; + + foreach($searchProperties as $propertyName=>$description) { + + $psp = $dom->createElement('d:principal-search-property'); + $root->appendChild($psp); + + $prop = $dom->createElement('d:prop'); + $psp->appendChild($prop); + + $propName = null; + preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); + + //if (!isset($nsList[$propName[1]])) { + // $nsList[$propName[1]] = 'x' . count($nsList); + //} + + // If the namespace was defined in the top-level xml namespaces, it means + // there was already a namespace declaration, and we don't have to worry about it. + //if (isset($server->xmlNamespaces[$propName[1]])) { + $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]); + //} else { + // $currentProperty = $dom->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); + //} + $prop->appendChild($currentProperty); + + $descriptionElem = $dom->createElement('d:description'); + $descriptionElem->setAttribute('xml:lang','en'); + $descriptionElem->appendChild($dom->createTextNode($description)); + $psp->appendChild($descriptionElem); + + + } + + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody($dom->saveXML()); + + } + + protected function principalPropertySearchReport($dom) { + + $searchableProperties = array( + '{DAV:}displayname' => 'display name' + + ); + + list($searchProperties, $requestedProperties) = $this->parsePrincipalPropertySearchReportRequest($dom); + + $uri = $this->server->getRequestUri(); + + $result = array(); + + $lookupResults = $this->server->getPropertiesForPath($uri, array_keys($searchProperties), 1); + + // The first item in the results is the parent, so we get rid of it. + array_shift($lookupResults); + + $matches = array(); + + foreach($lookupResults as $lookupResult) { + + foreach($searchProperties as $searchProperty=>$searchValue) { + if (!isset($searchableProperties[$searchProperty])) { + throw new Sabre_DAV_Exception_BadRequest('Searching for ' . $searchProperty . ' is not supported'); + } + + if (isset($lookupResult[200][$searchProperty]) && + mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8')!==false) { + $matches[] = $lookupResult['href']; + } + + } + + } + + $matchProperties = array(); + + foreach($matches as $match) { + + list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0); + $matchProperties[] = $result; + + } + + $xml = $this->server->generateMultiStatus($matchProperties); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->sendBody($xml); + + } + + protected function parsePrincipalPropertySearchReportRequest($dom) { + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth!==0) { + throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); + } + + $searchProperties = array(); + + // Parsing the search request + foreach($dom->firstChild->childNodes as $searchNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') + continue; + + $propertyName = null; + $propertyValue = null; + + foreach($searchNode->childNodes as $childNode) { + + switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) { + + case '{DAV:}prop' : + $property = Sabre_DAV_XMLUtil::parseProperties($searchNode); + reset($property); + $propertyName = key($property); + break; + + case '{DAV:}match' : + $propertyValue = $childNode->textContent; + break; + + } + + + } + + if (is_null($propertyName) || is_null($propertyValue)) + throw new Sabre_DAV_Exception_BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); + + $searchProperties[$propertyName] = $propertyValue; + + } + + return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild))); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php new file mode 100755 index 00000000..a69ce6f6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php @@ -0,0 +1,147 @@ +principalUri = $principalUri; + $this->principalProperties = $principalProperties; + + } + + /** + * Returns the name of the element + * + * @return void + */ + public function getName() { + + list(, $name) = Sabre_DAV_URLUtil::splitPath($this->principalUri); + return $name; + + } + + /** + * Returns the name of the user + * + * @return void + */ + public function getDisplayName() { + + if (isset($this->principalProperties['{DAV:}displayname'])) { + return $this->principalProperties['{DAV:}displayname']; + } else { + return $this->getName(); + } + + } + + /** + * Returns a list of properties + * + * @param array $requestedProperties + * @return void + */ + public function getProperties($requestedProperties) { + + if (!count($requestedProperties)) { + + // If all properties were requested + // we will only returns properties from this list + $requestedProperties = array( + '{DAV:}resourcetype', + '{DAV:}displayname', + ); + + } + + // We need to always return the resourcetype + // This is a bug in the core server, but it is easier to do it this way for now + $newProperties = array( + '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType('{DAV:}principal') + ); + foreach($requestedProperties as $propName) switch($propName) { + + case '{DAV:}alternate-URI-set' : + if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { + $href = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; + $newProperties[$propName] = new Sabre_DAV_Property_Href($href); + } else { + $newProperties[$propName] = null; + } + break; + case '{DAV:}group-member-set' : + case '{DAV:}group-membership' : + $newProperties[$propName] = null; + break; + + case '{DAV:}principal-URL' : + $newProperties[$propName] = new Sabre_DAV_Property_Href($this->principalUri); + break; + + case '{DAV:}displayname' : + $newProperties[$propName] = $this->getDisplayName(); + break; + + default : + if (isset($this->principalProperties[$propName])) { + $newProperties[$propName] = $this->principalProperties[$propName]; + } + break; + + } + + return $newProperties; + + + } + + /** + * Updates this principals properties. + * + * Currently this is not supported + * + * @param array $properties + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateProperties($properties) { + + return false; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php new file mode 100755 index 00000000..75cf3af9 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php @@ -0,0 +1,74 @@ +authBackend = $authBackend; + + } + + /** + * Returns the name of this collection. + * + * @return string + */ + public function getName() { + + return self::NODENAME; + + } + + /** + * Retursn the list of users + * + * @return void + */ + public function getChildren() { + + $children = array(); + foreach($this->authBackend->getUsers() as $principalInfo) { + + $principalUri = $principalInfo['uri'] . '/'; + $children[] = new Sabre_DAV_Auth_Principal($principalUri,$principalInfo); + + + } + return $children; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php new file mode 100755 index 00000000..430ebde8 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php @@ -0,0 +1,97 @@ + 'image/jpeg', + 'gif' => 'image/gif', + 'png' => 'image/png', + + // groupware + 'ics' => 'text/calendar', + 'vcf' => 'text/x-vcard', + + // text + 'txt' => 'text/plain', + + ); + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + // Using a relatively low priority (200) to allow other extensions + // to set the content-type first. + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200); + + } + + /** + * Handler for teh afterGetProperties event + * + * @param string $path + * @param array $properties + * @return void + */ + public function afterGetProperties($path, &$properties) { + + if (array_key_exists('{DAV:}getcontenttype', $properties[404])) { + + list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path); + $contentType = $this->getContentType($fileName); + + if ($contentType) { + $properties[200]['{DAV:}getcontenttype'] = $contentType; + unset($properties[404]['{DAV:}getcontenttype']); + } + + } + + } + + /** + * Simple method to return the contenttype + * + * @param string $fileName + * @return string + */ + protected function getContentType($fileName) { + + // Just grabbing the extension + $extension = substr($fileName,strrpos($fileName,'.')+1); + if (isset($this->extensionMap[$extension])) + return $this->extensionMap[$extension]; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php new file mode 100755 index 00000000..df60ae43 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php @@ -0,0 +1,54 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + } + + /** + * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request + * + * @param string $method + * @return bool + */ + public function httpGetInterceptor($method, $uri) { + + if ($method!='GET') return true; + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_IFile) return; + + $this->server->invokeMethod('PROPFIND',$uri); + return false; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php new file mode 100755 index 00000000..f718b4fe --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php @@ -0,0 +1,248 @@ +enablePost = $enablePost; + + } + + /** + * Initializes the plugin and subscribes to events + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler')); + } + + /** + * This method intercepts GET requests to collections and returns the html + * + * @param string $method + * @return bool + */ + public function httpGetInterceptor($method, $uri) { + + if ($method!='GET') return true; + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_IFile) return true; + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8'); + + $this->server->httpResponse->sendBody( + $this->generateDirectoryIndex($uri) + ); + + return false; + + } + + /** + * Handles POST requests for tree operations + * + * This method is not yet used. + * + * @param string $method + * @return bool + */ + public function httpPOSTHandler($method, $uri) { + + if ($method!='POST') return true; + if (isset($_POST['action'])) switch($_POST['action']) { + + case 'mkcol' : + if (isset($_POST['name']) && trim($_POST['name'])) { + // Using basename() because we won't allow slashes + list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($_POST['name'])); + $this->server->createDirectory($uri . '/' . $folderName); + } + break; + case 'put' : + if ($_FILES) $file = current($_FILES); + else break; + $newName = trim($file['name']); + list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name'])); + if (isset($_POST['name']) && trim($_POST['name'])) + $newName = trim($_POST['name']); + + // Making sure we only have a 'basename' component + list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName); + + + if (is_uploaded_file($file['tmp_name'])) { + $parent = $this->server->tree->getNodeForPath(trim($uri,'/')); + $parent->createFile($newName,fopen($file['tmp_name'],'r')); + } + + } + $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri()); + return false; + + } + + /** + * Escapes a string for html. + * + * @param string $value + * @return void + */ + public function escapeHTML($value) { + + return htmlspecialchars($value,ENT_QUOTES,'UTF-8'); + + } + + /** + * Generates the html directory index for a given url + * + * @param string $path + * @return string + */ + public function generateDirectoryIndex($path) { + + $html = " + + Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . " + + + +

                        Index for " . $this->escapeHTML($path) . "/

                        + + + "; + + $files = $this->server->getPropertiesForPath($path,array( + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ),1); + + foreach($files as $k=>$file) { + + // This is the current directory, we can skip it + if (rtrim($file['href'],'/')==$path) continue; + + list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); + + $type = null; + + if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); + + // resourcetype can have multiple values + if (is_array($type)) { + $type = implode(', ', $type); + } + + // Some name mapping is preferred + switch($type) { + case '{DAV:}collection' : + $type = 'Collection'; + break; + } + } + + // If no resourcetype was found, we attempt to use + // the contenttype property + if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + $type = $file[200]['{DAV:}getcontenttype']; + } + if (!$type) $type = 'Unknown'; + + $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; + $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):''; + + $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); + + $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; + + $name = $this->escapeHTML($name); + $displayName = $this->escapeHTML($displayName); + $type = $this->escapeHTML($type); + + $html.= " + + + + +"; + + } + + $html.= ""; + + if ($this->enablePost) { + $html.= ''; + } + + $html.= "
                        NameTypeSizeLast modified

                        {$displayName}{$type}{$size}{$lastmodified}

                        +

                        Create new folder

                        + + Name:
                        + +
                        +
                        +

                        Upload file

                        + + Name (optional):
                        + File:
                        + +
                        +
                        +
                        Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2010 http://code.google.com/p/sabredav/
                        + +"; + + return $html; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php b/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php new file mode 100755 index 00000000..ebc266e1 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php @@ -0,0 +1,90 @@ +getChildren() as $child) { + + if ($child->getName()==$name) return $child; + + } + throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name); + + } + + /** + * Checks is a child-node exists. + * + * It is generally a good idea to try and override this. Usually it can be optimized. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + try { + + $this->getChild($name); + return true; + + } catch(Sabre_DAV_Exception_FileNotFound $e) { + + return false; + + } + + } + + /** + * Creates a new file in the directory + * + * @param string $name Name of the file + * @param resource $data Initial payload, passed as a readable stream resource. + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createFile($name, $data = null) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')'); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory'); + + } + + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php new file mode 100755 index 00000000..46b710a0 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php @@ -0,0 +1,63 @@ +lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); + $errorNode->appendChild($error); + if (!is_object($this->lock)) var_dump($this->lock); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php new file mode 100755 index 00000000..cd03d4c7 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php @@ -0,0 +1,28 @@ +ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); + $errorNode->appendChild($error); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php new file mode 100755 index 00000000..059ee346 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php @@ -0,0 +1,39 @@ +message = 'The locktoken supplied does not match any locks on this entity'; + + } + + /** + * This method allows the exception to include additonal information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri'); + $errorNode->appendChild($error); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php new file mode 100755 index 00000000..3f15ea74 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php @@ -0,0 +1,67 @@ +lock = $lock; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + public function getHTTPCode() { + + return 423; + + } + + /** + * This method allows the exception to include additonal information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + if ($this->lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); + $errorNode->appendChild($error); + if (!is_object($this->lock)) var_dump($this->lock); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php new file mode 100755 index 00000000..67ad9a8a --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php @@ -0,0 +1,44 @@ +getAllowedMethods($server->getRequestUri()); + + return array( + 'Allow' => strtoupper(implode(', ',$methods)), + ); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php new file mode 100755 index 00000000..28159853 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php @@ -0,0 +1,28 @@ +header = $header; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + public function getHTTPCode() { + + return 412; + + } + + /** + * This method allows the exception to include additonal information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + if ($this->header) { + $prop = $errorNode->ownerDocument->createElement('s:header'); + $prop->nodeValue = $this->header; + $errorNode->appendChild($prop); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php new file mode 100755 index 00000000..64526f42 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php @@ -0,0 +1,30 @@ +ownerDocument->createElementNS('DAV:','d:supported-report'); + $errorNode->appendChild($error); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php new file mode 100755 index 00000000..723138d8 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php @@ -0,0 +1,29 @@ +path . '/' . $name; + file_put_contents($newPath,$data); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + $newPath = $this->path . '/' . $name; + mkdir($newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File with name ' . $path . ' could not be located'); + + if (is_dir($path)) { + + return new Sabre_DAV_FS_Directory($path); + + } else { + + return new Sabre_DAV_FS_File($path); + + } + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $nodes = array(); + foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); + return $nodes; + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return void + */ + public function delete() { + + foreach($this->getChildren() as $child) $child->delete(); + rmdir($this->path); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + public function getQuotaInfo() { + + return array( + disk_total_space($this->path)-disk_free_space($this->path), + disk_free_space($this->path) + ); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php new file mode 100755 index 00000000..dc2e7f98 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php @@ -0,0 +1,89 @@ +path,$data); + + } + + /** + * Returns the data + * + * @return string + */ + public function get() { + + return fopen($this->path,'r'); + + } + + /** + * Delete the current file + * + * @return void + */ + public function delete() { + + unlink($this->path); + + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + public function getSize() { + + return filesize($this->path); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * + * @return mixed + */ + public function getETag() { + + return null; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return mixed + */ + public function getContentType() { + + return null; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php b/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php new file mode 100755 index 00000000..172af852 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php @@ -0,0 +1,81 @@ +path = $path; + + } + + + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + + list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path); + return $name; + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) { + + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); + + $newPath = $parentPath . '/' . $newName; + rename($this->path,$newPath); + + $this->path = $newPath; + + } + + + + /** + * Returns the last modification time, as a unix timestamp + * + * @return int + */ + public function getLastModified() { + + return filemtime($this->path); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php new file mode 100755 index 00000000..b42a9a9d --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php @@ -0,0 +1,135 @@ +path . '/' . $name; + file_put_contents($newPath,$data); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + // We're not allowing dots + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + $newPath = $this->path . '/' . $name; + mkdir($newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File could not be located'); + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + + if (is_dir($path)) { + + return new Sabre_DAV_FSExt_Directory($path); + + } else { + + return new Sabre_DAV_FSExt_File($path); + + } + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + if ($name=='.' || $name=='..') + throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $nodes = array(); + foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node); + return $nodes; + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return void + */ + public function delete() { + + // Deleting all children + foreach($this->getChildren() as $child) $child->delete(); + + // Removing resource info, if its still around + if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav'); + + // Removing the directory itself + rmdir($this->path); + + return parent::delete(); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + public function getQuotaInfo() { + + return array( + disk_total_space($this->path)-disk_free_space($this->path), + disk_free_space($this->path) + ); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php new file mode 100755 index 00000000..987bca33 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php @@ -0,0 +1,88 @@ +path,$data); + + } + + /** + * Returns the data + * + * @return string + */ + public function get() { + + return fopen($this->path,'r'); + + } + + /** + * Delete the current file + * + * @return void + */ + public function delete() { + + unlink($this->path); + return parent::delete(); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + */ + public function getETag() { + + return '"' . md5_file($this->path). '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + */ + public function getContentType() { + + return null; + + } + + /** + * Returns the size of the file, in bytes + * + * @return int + */ + public function getSize() { + + return filesize($this->path); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php new file mode 100755 index 00000000..0af346d1 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php @@ -0,0 +1,276 @@ +getResourceData(); + $locks = $resourceData['locks']; + foreach($locks as $k=>$lock) { + if (time() > $lock->timeout + $lock->created) unset($locks[$k]); + } + return $locks; + + } + + /** + * Locks this node + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return void + */ + function lock(Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + + $resourceData = $this->getResourceData(); + if (!isset($resourceData['locks'])) $resourceData['locks'] = array(); + $current = null; + foreach($resourceData['locks'] as $k=>$lock) { + if ($lock->token === $lockInfo->token) $current = $k; + } + if (!is_null($current)) $resourceData['locks'][$current] = $lockInfo; + else $resourceData['locks'][] = $lockInfo; + + $this->putResourceData($resourceData); + + } + + /** + * Removes a lock from this node + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + function unlock(Sabre_DAV_Locks_LockInfo $lockInfo) { + + //throw new Sabre_DAV_Exception('bla'); + $resourceData = $this->getResourceData(); + foreach($resourceData['locks'] as $k=>$lock) { + + if ($lock->token === $lockInfo->token) { + + unset($resourceData['locks'][$k]); + $this->putResourceData($resourceData); + return true; + + } + } + return false; + + } + + /** + * Updates properties on this node, + * + * @param array $mutations + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateProperties($properties) { + + $resourceData = $this->getResourceData(); + + $result = array(); + + foreach($properties as $propertyName=>$propertyValue) { + + // If it was null, we need to delete the property + if (is_null($propertyValue)) { + if (isset($resourceData['properties'][$propertyName])) { + unset($resourceData['properties'][$propertyName]); + } + } else { + $resourceData['properties'][$propertyName] = $propertyValue; + } + + } + + $this->putResourceData($resourceData); + return true; + } + + /** + * Returns a list of properties for this nodes.; + * + * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author + * If the array is empty, all properties should be returned + * + * @param array $properties + * @return void + */ + function getProperties($properties) { + + $resourceData = $this->getResourceData(); + + // if the array was empty, we need to return everything + if (!$properties) return $resourceData['properties']; + + $props = array(); + foreach($properties as $property) { + if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; + } + + return $props; + + } + + /** + * Returns the path to the resource file + * + * @return string + */ + protected function getResourceInfoPath() { + + list($parentDir) = Sabre_DAV_URLUtil::splitPath($this->path); + return $parentDir . '/.sabredav'; + + } + + /** + * Returns all the stored resource information + * + * @return array + */ + protected function getResourceData() { + + $path = $this->getResourceInfoPath(); + if (!file_exists($path)) return array('locks'=>array(), 'properties' => array()); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'r'); + flock($handle,LOCK_SH); + $data = ''; + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!isset($data[$this->getName()])) { + return array('locks'=>array(), 'properties' => array()); + } + + $data = $data[$this->getName()]; + if (!isset($data['locks'])) $data['locks'] = array(); + if (!isset($data['properties'])) $data['properties'] = array(); + return $data; + + } + + /** + * Updates the resource information + * + * @param array $newData + * @return void + */ + protected function putResourceData(array $newData) { + + $path = $this->getResourceInfoPath(); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + $data = ''; + + rewind($handle); + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + $data[$this->getName()] = $newData; + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($data)); + fclose($handle); + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) { + + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); + $newPath = $parentPath . '/' . $newName; + + // We're deleting the existing resourcedata, and recreating it + // for the new path. + $resourceData = $this->getResourceData(); + $this->deleteResourceData(); + + rename($this->path,$newPath); + $this->path = $newPath; + $this->putResourceData($resourceData); + + + } + + public function deleteResourceData() { + + // When we're deleting this node, we also need to delete any resource information + $path = $this->getResourceInfoPath(); + if (!file_exists($path)) return true; + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + $data = ''; + + rewind($handle); + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (isset($data[$this->getName()])) unset($data[$this->getName()]); + ftruncate($handle,0); + rewind($handle); + fwrite($handle,serialize($data)); + fclose($handle); + + } + + public function delete() { + + return $this->deleteResourceData(); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/File.php new file mode 100755 index 00000000..aaaf88e6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/File.php @@ -0,0 +1,81 @@ + array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param array $mutations + * @return bool|array + */ + function updateProperties($properties); + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname + * + * If the array is empty, it means 'all properties' were requested. + * + * @param array $properties + * @return void + */ + function getProperties($properties); + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php b/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php new file mode 100755 index 00000000..afba5efd --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php @@ -0,0 +1,27 @@ +dataDir = $dataDir; + + } + + protected function getFileNameForUri($uri) { + + return $this->dataDir . '/sabredav_' . md5($uri) . '.locks'; + + } + + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * @param string $uri + * @return array + */ + public function getLocks($uri) { + + $lockList = array(); + $currentPath = ''; + + foreach(explode('/',$uri) as $uriPart) { + + // weird algorithm that can probably be improved, but we're traversing the path top down + if ($currentPath) $currentPath.='/'; + $currentPath.=$uriPart; + + $uriLocks = $this->getData($currentPath); + + foreach($uriLocks as $uriLock) { + + // Unless we're on the leaf of the uri-tree we should ingore locks with depth 0 + if($uri==$currentPath || $uriLock->depth!=0) { + $uriLock->uri = $currentPath; + $lockList[] = $uriLock; + } + + } + + } + + // Checking if we can remove any of these locks + foreach($lockList as $k=>$lock) { + if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); + } + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + + $locks = $this->getLocks($uri); + foreach($locks as $k=>$lock) { + if ($lock->token == $lockInfo->token) unset($locks[$k]); + } + $locks[] = $lockInfo; + $this->putData($uri,$locks); + return true; + + } + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + $locks = $this->getLocks($uri); + foreach($locks as $k=>$lock) { + + if ($lock->token == $lockInfo->token) { + + unset($locks[$k]); + $this->putData($uri,$locks); + return true; + + } + } + return false; + + } + + /** + * Returns the stored data for a uri + * + * @param string $uri + * @return array + */ + protected function getData($uri) { + + $path = $this->getFilenameForUri($uri); + if (!file_exists($path)) return array(); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'r'); + flock($handle,LOCK_SH); + $data = ''; + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!$data) return array(); + return $data; + + } + + /** + * Updates the lock information + * + * @param string $uri + * @param array $newData + * @return void + */ + protected function putData($uri,array $newData) { + + $path = $this->getFileNameForUri($uri); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($newData)); + fclose($handle); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php new file mode 100755 index 00000000..11c7fa96 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php @@ -0,0 +1,141 @@ +pdo = $pdo; + + } + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * @param string $uri + * @return array + */ + public function getLocks($uri) { + + // NOTE: the following 10 lines or so could be easily replaced by + // pure sql. MySQL's non-standard string concatination prevents us + // from doing this though. + $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM locks WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; + $params = array(time(),$uri); + + // We need to check locks for every part in the uri. + $uriParts = explode('/',$uri); + + // We already covered the last part of the uri + array_pop($uriParts); + + $currentPath=''; + + foreach($uriParts as $part) { + + if ($currentPath) $currentPath.='/'; + $currentPath.=$part; + + $query.=' OR (depth!=0 AND uri = ?)'; + $params[] = $currentPath; + + } + + $query.=')'; + + $stmt = $this->pdo->prepare($query); + $stmt->execute($params); + $result = $stmt->fetchAll(); + + $lockList = array(); + foreach($result as $row) { + + $lockInfo = new Sabre_DAV_Locks_LockInfo(); + $lockInfo->owner = $row['owner']; + $lockInfo->token = $row['token']; + $lockInfo->timeout = $row['timeout']; + $lockInfo->created = $row['created']; + $lockInfo->scope = $row['scope']; + $lockInfo->depth = $row['depth']; + $lockInfo->uri = $row['uri']; + $lockList[] = $lockInfo; + + } + + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 30*60; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getLocks($uri); + $exists = false; + foreach($locks as $k=>$lock) { + if ($lock->token == $lockInfo->token) $exists = true; + } + + if ($exists) { + $stmt = $this->pdo->prepare('UPDATE locks SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); + $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); + } else { + $stmt = $this->pdo->prepare('INSERT INTO locks (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); + $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); + } + + return true; + + } + + + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + $stmt = $this->pdo->prepare('DELETE FROM locks WHERE uri = ? AND token = ?'); + $stmt->execute(array($uri,$lockInfo->token)); + + return $stmt->rowCount()===1; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php new file mode 100755 index 00000000..aa174384 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php @@ -0,0 +1,81 @@ +addPlugin($lockPlugin); + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { + + /** + * locksBackend + * + * @var Sabre_DAV_Locks_Backend_Abstract + */ + private $locksBackend; + + /** + * server + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * __construct + * + * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend + * @return void + */ + public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) { + + $this->locksBackend = $locksBackend; + + } + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50); + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + + } + + /** + * This method is called by the Server if the user used an HTTP method + * the server didn't recognize. + * + * This plugin intercepts the LOCK and UNLOCK methods. + * + * @param string $method + * @return bool + */ + public function unknownMethod($method, $uri) { + + switch($method) { + + case 'LOCK' : $this->httpLock($uri); return false; + case 'UNLOCK' : $this->httpUnlock($uri); return false; + + } + + } + + /** + * This method is called after most properties have been found + * it allows us to add in any Lock-related properties + * + * @param string $path + * @param array $properties + * @return bool + */ + public function afterGetProperties($path,&$newProperties) { + + foreach($newProperties[404] as $propName=>$discard) { + + $node = null; + + switch($propName) { + + case '{DAV:}supportedlock' : + $val = false; + if ($this->locksBackend) $val = true; + else { + if (!$node) $node = $this->server->tree->getNodeForPath($path); + if ($node instanceof Sabre_DAV_ILockable) $val = true; + } + $newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val); + unset($newProperties[404][$propName]); + break; + + case '{DAV:}lockdiscovery' : + $newProperties[200][$propName] = new Sabre_DAV_Property_LockDiscovery($this->getLocks($path)); + unset($newProperties[404][$propName]); + break; + + } + + + } + return true; + + } + + + /** + * This method is called before the logic for any HTTP method is + * handled. + * + * This plugin uses that feature to intercept access to locked resources. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + switch($method) { + + case 'DELETE' : + case 'MKCOL' : + case 'PROPPATCH' : + case 'PUT' : + $lastLock = null; + if (!$this->validateLock($uri,$lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'MOVE' : + $lastLock = null; + if (!$this->validateLock(array( + $uri, + $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), + ),$lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'COPY' : + $lastLock = null; + if (!$this->validateLock( + $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), + $lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + } + + return true; + + } + + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + if ($this->locksBackend || + $this->server->tree->getNodeForPath($uri) instanceof Sabre_DAV_ILocks) { + return array('LOCK','UNLOCK'); + } + return array(); + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * In this case this is only the number 2. The 2 in the Dav: header + * indicates the server supports locks. + * + * @return array + */ + public function getFeatures() { + + return array(2); + + } + + /** + * Returns all lock information on a particular uri + * + * This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array. + * + * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree + * + * @param string $uri + * @return array + */ + public function getLocks($uri) { + + $lockList = array(); + $currentPath = ''; + foreach(explode('/',$uri) as $uriPart) { + + $uriLocks = array(); + if ($currentPath) $currentPath.='/'; + $currentPath.=$uriPart; + + try { + + $node = $this->server->tree->getNodeForPath($currentPath); + if ($node instanceof Sabre_DAV_ILockable) $uriLocks = $node->getLocks(); + + } catch (Sabre_DAV_Exception_FileNotFound $e){ + // In case the node didn't exist, this could be a lock-null request + } + + foreach($uriLocks as $uriLock) { + + // Unless we're on the leaf of the uri-tree we should ingore locks with depth 0 + if($uri==$currentPath || $uriLock->depth!=0) { + $uriLock->uri = $currentPath; + $lockList[] = $uriLock; + } + + } + + } + if ($this->locksBackend) $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri)); + return $lockList; + + } + + /** + * Locks an uri + * + * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock + * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type + * of lock (shared or exclusive) and the owner of the lock + * + * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock + * + * Additionally, a lock can be requested for a non-existant file. In these case we're obligated to create an empty file as per RFC4918:S7.3 + * + * @param string $uri + * @return void + */ + protected function httpLock($uri) { + + $lastLock = null; + if (!$this->validateLock($uri,$lastLock)) { + + // If the existing lock was an exclusive lock, we need to fail + if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) { + //var_dump($lastLock); + throw new Sabre_DAV_Exception_ConflictingLock($lastLock); + } + + } + + if ($body = $this->server->httpRequest->getBody(true)) { + // This is a new lock request + $lockInfo = $this->parseLockRequest($body); + $lockInfo->depth = $this->server->getHTTPDepth(); + $lockInfo->uri = $uri; + if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock); + + } elseif ($lastLock) { + + // This must have been a lock refresh + $lockInfo = $lastLock; + + // The resource could have been locked through another uri. + if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; + + } else { + + // There was neither a lock refresh nor a new lock request + throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests'); + + } + + if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; + + $newFile = false; + + // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first + try { + $node = $this->server->tree->getNodeForPath($uri); + + // We need to call the beforeWriteContent event for RFC3744 + $this->server->broadcastEvent('beforeWriteContent',array($uri)); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + // It didn't, lets create it + $this->server->createFile($uri,fopen('php://memory','r')); + $newFile = true; + + } + + $this->lockNode($uri,$lockInfo); + + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Lock-Token','token . '>'); + $this->server->httpResponse->sendStatus($newFile?201:200); + $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo)); + + } + + /** + * Unlocks a uri + * + * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header + * The server should return 204 (No content) on success + * + * @param string $uri + * @return void + */ + protected function httpUnlock($uri) { + + $lockToken = $this->server->httpRequest->getHeader('Lock-Token'); + + // If the locktoken header is not supplied, we need to throw a bad request exception + if (!$lockToken) throw new Sabre_DAV_Exception_BadRequest('No lock token was supplied'); + + $locks = $this->getLocks($uri); + + // We're grabbing the node information, just to rely on the fact it will throw a 404 when the node doesn't exist + //$this->server->tree->getNodeForPath($uri); + + foreach($locks as $lock) { + + if ('token . '>' == $lockToken) { + + $this->unlockNode($uri,$lock); + $this->server->httpResponse->setHeader('Content-Length','0'); + $this->server->httpResponse->sendStatus(204); + return; + + } + + } + + // If we got here, it means the locktoken was invalid + throw new Sabre_DAV_Exception_LockTokenMatchesRequestUri(); + + } + + /** + * Locks a uri + * + * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored + * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return void + */ + public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return; + + try { + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_ILockable) return $node->lock($lockInfo); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + // In case the node didn't exist, this could be a lock-null request + } + if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); + throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); + + } + + /** + * Unlocks a uri + * + * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return void + */ + public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return; + try { + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_ILockable) return $node->unlock($lockInfo); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + // In case the node didn't exist, this could be a lock-null request + } + + if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); + + } + + + /** + * Returns the contents of the HTTP Timeout header. + * + * The method formats the header into an integer. + * + * @return int + */ + public function getTimeoutHeader() { + + $header = $this->server->httpRequest->getHeader('Timeout'); + + if ($header) { + + if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); + else if (strtolower($header)=='infinite') $header=Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE; + else throw new Sabre_DAV_Exception_BadRequest('Invalid HTTP timeout header'); + + } else { + + $header = 0; + + } + + return $header; + + } + + /** + * Generates the response for successfull LOCK requests + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return string + */ + protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) { + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + + $prop = $dom->createElementNS('DAV:','d:prop'); + $dom->appendChild($prop); + + $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery'); + $prop->appendChild($lockDiscovery); + + $lockObj = new Sabre_DAV_Property_LockDiscovery(array($lockInfo),true); + $lockObj->serialize($this->server,$lockDiscovery); + + return $dom->saveXML(); + + } + + /** + * validateLock should be called when a write operation is about to happen + * It will check if the requested url is locked, and see if the correct lock tokens are passed + * + * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri + * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo) + * @return bool + */ + protected function validateLock($urls = null,&$lastLock = null) { + + if (is_null($urls)) { + $urls = array($this->server->getRequestUri()); + } elseif (is_string($urls)) { + $urls = array($urls); + } elseif (!is_array($urls)) { + throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array'); + } + + $conditions = $this->getIfConditions(); + + // We're going to loop through the urls and make sure all lock conditions are satisfied + foreach($urls as $url) { + + $locks = $this->getLocks($url); + + // If there were no conditions, but there were locks, we fail + if (!$conditions && $locks) { + reset($locks); + $lastLock = current($locks); + return false; + } + + // If there were no locks or conditions, we go to the next url + if (!$locks && !$conditions) continue; + + foreach($conditions as $condition) { + + $conditionUri = $condition['uri']?$this->server->calculateUri($condition['uri']):''; + + // If the condition has a url, and it isn't part of the affected url at all, check the next condition + if ($conditionUri && strpos($url,$conditionUri)!==0) continue; + + // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken + // At least 1 condition has to be satisfied + foreach($condition['tokens'] as $conditionToken) { + + $etagValid = true; + $lockValid = true; + + // key 2 can contain an etag + if ($conditionToken[2]) { + + $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); + $node = $this->server->tree->getNodeForPath($uri); + $etagValid = $node->getETag()==$conditionToken[2]; + + } + + // key 1 can contain a lock token + if ($conditionToken[1]) { + + $lockValid = false; + // Match all the locks + foreach($locks as $lockIndex=>$lock) { + + $lockToken = 'opaquelocktoken:' . $lock->token; + + // Checking NOT + if (!$conditionToken[0] && $lockToken != $conditionToken[1]) { + + // Condition valid, onto the next + $lockValid = true; + break; + } + if ($conditionToken[0] && $lockToken == $conditionToken[1]) { + + $lastLock = $lock; + // Condition valid and lock matched + unset($locks[$lockIndex]); + $lockValid = true; + break; + + } + + } + + } + + // If, after checking both etags and locks they are stil valid, + // we can continue with the next condition. + if ($etagValid && $lockValid) continue 2; + } + // No conditions matched, so we fail + throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match','If'); + } + + // Conditions were met, we'll also need to check if all the locks are gone + if (count($locks)) { + + reset($locks); + + // There's still locks, we fail + $lastLock = current($locks); + return false; + + } + + + } + + // We got here, this means every condition was satisfied + return true; + + } + + /** + * This method is created to extract information from the WebDAV HTTP 'If:' header + * + * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information + * The function will return an array, containg structs with the following keys + * + * * uri - the uri the condition applies to. This can be an empty string for 'every relevant url' + * * tokens - The lock token. another 2 dimensional array containg 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) + * * etag - an etag, if supplied + * + * @return void + */ + public function getIfConditions() { + + $header = $this->server->httpRequest->getHeader('If'); + if (!$header) return array(); + + $matches = array(); + + $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; + preg_match_all($regex,$header,$matches,PREG_SET_ORDER); + + $conditions = array(); + + foreach($matches as $match) { + + $condition = array( + 'uri' => $match['uri'], + 'tokens' => array( + array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'') + ), + ); + + if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array( + $match['not']?0:1, + $match['token'], + isset($match['etag'])?$match['etag']:'' + ); + else { + $conditions[] = $condition; + } + + } + + return $conditions; + + } + + /** + * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object + * + * @param string $body + * @return Sabre_DAV_Locks_LockInfo + */ + protected function parseLockRequest($body) { + + $xml = simplexml_load_string($body,null,LIBXML_NOWARNING); + $xml->registerXPathNamespace('d','DAV:'); + $lockInfo = new Sabre_DAV_Locks_LockInfo(); + + $lockInfo->owner = (string)$xml->owner; + + $lockToken = '44445502'; + $id = md5(microtime() . 'somethingrandom'); + $lockToken.='-' . substr($id,0,4) . '-' . substr($id,4,4) . '-' . substr($id,8,4) . '-' . substr($id,12,12); + + $lockInfo->token = $lockToken; + $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0?Sabre_DAV_Locks_LockInfo::EXCLUSIVE:Sabre_DAV_Locks_LockInfo::SHARED; + + return $lockInfo; + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php new file mode 100755 index 00000000..c2dc4296 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php @@ -0,0 +1,79 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?mount + * + * @param string $method + * @return void + */ + public function beforeMethod($method, $uri) { + + if ($method!='GET') return; + if ($this->server->httpRequest->getQueryString()!='mount') return; + + $currentUri = $this->server->httpRequest->getAbsoluteUri(); + + // Stripping off everything after the ? + list($currentUri) = explode('?',$currentUri); + + $this->davMount($currentUri); + + // Returning false to break the event chain + return false; + + } + + /** + * Generates the davmount response + * + * @param string $uri absolute uri + * @return void + */ + public function davMount($uri) { + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml'); + ob_start(); + echo '', "\n"; + echo "\n"; + echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; + echo ""; + $this->server->httpResponse->sendBody(ob_get_clean()); + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Node.php b/3.0/modules/webdav/libraries/Sabre/DAV/Node.php new file mode 100755 index 00000000..8943b786 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Node.php @@ -0,0 +1,55 @@ +rootNode = $rootNode; + + } + + /** + * Returns the INode object for the requested path + * + * @param string $path + * @return Sabre_DAV_INode + */ + public function getNodeForPath($path) { + + $path = trim($path,'/'); + if (isset($this->cache[$path])) return $this->cache[$path]; + + //if (!$path || $path=='.') return $this->rootNode; + $currentNode = $this->rootNode; + $i=0; + // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. + foreach(explode('/',$path) as $pathPart) { + + // If this part of the path is just a dot, it actually means we can skip it + if ($pathPart=='.' || $pathPart=='') continue; + + if (!($currentNode instanceof Sabre_DAV_ICollection)) + throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path); + + $currentNode = $currentNode->getChild($pathPart); + + } + + $this->cache[$path] = $currentNode; + return $currentNode; + + } + + /** + * This function allows you to check if a node exists. + * + * @param string $path + * @return bool + */ + public function nodeExists($path) { + + try { + + list($parent, $base) = Sabre_DAV_URLUtil::splitPath($path); + $parentNode = $this->getNodeForPath($parent); + return $parentNode->childExists($base); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + return false; + + } + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + public function getChildren($path) { + + $node = $this->getNodeForPath($path); + $children = $node->getChildren(); + foreach($children as $child) { + + $this->cache[trim($path,'/') . '/' . $child->getName()] = $child; + + } + return $children; + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + public function markDirty($path) { + + // We don't care enough about sub-paths + // flushing the entire cache + $path = trim($path,'/'); + foreach($this->cache as $nodePath=>$node) { + if ($nodePath == $path || strpos($nodePath,$path.'/')===0) + unset($this->cache[$nodePath]); + + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property.php new file mode 100755 index 00000000..ae0a64c6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property.php @@ -0,0 +1,25 @@ +time = $time; + } elseif (is_int($time) || ctype_digit($time)) { + $this->time = new DateTime('@' . $time); + } else { + $this->time = new DateTime($time); + } + + // Setting timezone to UTC + $this->time->setTimezone(new DateTimeZone('UTC')); + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { + + $doc = $prop->ownerDocument; + $prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); + $prop->setAttribute('b:dt','dateTime.rfc1123'); + $prop->nodeValue = $this->time->format(DateTime::RFC1123); + + } + + /** + * getTime + * + * @return DateTime + */ + public function getTime() { + + return $this->time; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php new file mode 100755 index 00000000..8b9400fb --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php @@ -0,0 +1,91 @@ +href = $href; + $this->autoPrefix = $autoPrefix; + + } + + /** + * Returns the uri + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Serializes this property. + * + * It will additionally prepend the href property with the server's base uri. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + + $prefix = $server->xmlNamespaces['DAV:']; + + $elem = $dom->ownerDocument->createElement($prefix . ':href'); + $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $this->href; + $dom->appendChild($elem); + + } + + /** + * Unserializes this property from a DOM Element + * + * This method returns an instance of this class. + * It will only decode {DAV:}href values. For non-compatible elements null will be returned. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_Href + */ + static function unserialize(DOMElement $dom) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { + return new self($dom->firstChild->textContent,false); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php new file mode 100755 index 00000000..56503acd --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php @@ -0,0 +1,25 @@ +locks = $locks; + $this->revealLockToken = $revealLockToken; + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $doc = $prop->ownerDocument; + + foreach($this->locks as $lock) { + + $activeLock = $doc->createElementNS('DAV:','d:activelock'); + $prop->appendChild($activeLock); + + $lockScope = $doc->createElementNS('DAV:','d:lockscope'); + $activeLock->appendChild($lockScope); + + $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==Sabre_DAV_Locks_LockInfo::EXCLUSIVE?'exclusive':'shared'))); + + $lockType = $doc->createElementNS('DAV:','d:locktype'); + $activeLock->appendChild($lockType); + + $lockType->appendChild($doc->createElementNS('DAV:','d:write')); + + $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == Sabre_DAV_Server::DEPTH_INFINITY?'infinity':$lock->depth))); + $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout)); + + if ($this->revealLockToken) { + $lockToken = $doc->createElementNS('DAV:','d:locktoken'); + $activeLock->appendChild($lockToken); + $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); + } + + $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); + + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php new file mode 100755 index 00000000..df3f7848 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php @@ -0,0 +1,126 @@ +type = $type; + + if ($type===self::HREF && is_null($href)) { + throw new Sabre_DAV_Exception('The href argument must be specified for the HREF principal type.'); + } + $this->href = $href; + + } + + /** + * Returns the principal type + * + * @return int + */ + public function getType() { + + return $this->type; + + } + + /** + * Returns the principal uri. + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Serializes the property into a DOMElement. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $node) { + + $prefix = $server->xmlNamespaces['DAV:']; + switch($this->type) { + + case self::UNAUTHENTICATED : + $node->appendChild( + $node->ownerDocument->createElement($prefix . ':unauthenticated') + ); + break; + case self::AUTHENTICATED : + $node->appendChild( + $node->ownerDocument->createElement($prefix . ':authenticated') + ); + break; + case self::HREF : + $href = $node->ownerDocument->createElement($prefix . ':href'); + $href->nodeValue = $server->getBaseUri() . $this->href; + $node->appendChild($href); + break; + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php new file mode 100755 index 00000000..51022a48 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php @@ -0,0 +1,80 @@ +resourceType = null; + elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY) + $this->resourceType = '{DAV:}collection'; + else + $this->resourceType = $resourceType; + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $propName = null; + $rt = $this->resourceType; + if (!is_array($rt)) $rt = array($rt); + + foreach($rt as $resourceType) { + if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { + + if (isset($server->xmlNamespaces[$propName[1]])) { + $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); + } else { + $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); + } + + } + } + + } + + /** + * Returns the value in clark-notation + * + * For example '{DAV:}collection' + * + * @return string + */ + public function getValue() { + + return $this->resourceType; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php new file mode 100755 index 00000000..6c75e8df --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php @@ -0,0 +1,156 @@ +href = $href; + $this->responseProperties = $responseProperties; + + } + + /** + * Returns the url + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Returns the property list + * + * @return array + */ + public function getResponseProperties() { + + return $this->responseProperties; + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + + $document = $dom->ownerDocument; + $properties = $this->responseProperties; + + $xresponse = $document->createElement('d:response'); + $dom->appendChild($xresponse); + + $uri = Sabre_DAV_URLUtil::encodePath($this->href); + + // Adding the baseurl to the beginning of the url + $uri = $server->getBaseUri() . $uri; + + $xresponse->appendChild($document->createElement('d:href',$uri)); + + // The properties variable is an array containing properties, grouped by + // HTTP status + foreach($properties as $httpStatus=>$propertyGroup) { + + // The 'href' is also in this array, and it's special cased. + // We will ignore it + if ($httpStatus=='href') continue; + + // If there are no properties in this group, we can also just carry on + if (!count($propertyGroup)) continue; + + $xpropstat = $document->createElement('d:propstat'); + $xresponse->appendChild($xpropstat); + + $xprop = $document->createElement('d:prop'); + $xpropstat->appendChild($xprop); + + $nsList = $server->xmlNamespaces; + + foreach($propertyGroup as $propertyName=>$propertyValue) { + + $propName = null; + preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); + + // special case for empty namespaces + if ($propName[1]=='') { + + $currentProperty = $document->createElement($propName[2]); + $xprop->appendChild($currentProperty); + $currentProperty->setAttribute('xmlns',''); + + } else { + + if (!isset($nsList[$propName[1]])) { + $nsList[$propName[1]] = 'x' . count($nsList); + } + + // If the namespace was defined in the top-level xml namespaces, it means + // there was already a namespace declaration, and we don't have to worry about it. + if (isset($server->xmlNamespaces[$propName[1]])) { + $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); + } else { + $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); + } + $xprop->appendChild($currentProperty); + + } + + if (is_scalar($propertyValue)) { + $text = $document->createTextNode($propertyValue); + $currentProperty->appendChild($text); + } elseif ($propertyValue instanceof Sabre_DAV_Property) { + $propertyValue->serialize($server,$currentProperty); + } elseif (!is_null($propertyValue)) { + throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); + } + + } + + $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus))); + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php new file mode 100755 index 00000000..0b7ca0cf --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php @@ -0,0 +1,76 @@ +supportsLocks = $supportsLocks; + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $doc = $prop->ownerDocument; + + if (!$this->supportsLocks) return null; + + $lockEntry1 = $doc->createElementNS('DAV:','d:lockentry'); + $lockEntry2 = $doc->createElementNS('DAV:','d:lockentry'); + + $prop->appendChild($lockEntry1); + $prop->appendChild($lockEntry2); + + $lockScope1 = $doc->createElementNS('DAV:','d:lockscope'); + $lockScope2 = $doc->createElementNS('DAV:','d:lockscope'); + $lockType1 = $doc->createElementNS('DAV:','d:locktype'); + $lockType2 = $doc->createElementNS('DAV:','d:locktype'); + + $lockEntry1->appendChild($lockScope1); + $lockEntry1->appendChild($lockType1); + $lockEntry2->appendChild($lockScope2); + $lockEntry2->appendChild($lockType2); + + $lockScope1->appendChild($doc->createElementNS('DAV:','d:exclusive')); + $lockScope2->appendChild($doc->createElementNS('DAV:','d:shared')); + + $lockType1->appendChild($doc->createElementNS('DAV:','d:write')); + $lockType2->appendChild($doc->createElementNS('DAV:','d:write')); + + //$frag->appendXML(''); + //$frag->appendXML(''); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php new file mode 100755 index 00000000..8676f4c0 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php @@ -0,0 +1,110 @@ +addReport($reports); + + } + + /** + * Adds a report to this property + * + * The report must be a string in clark-notation. + * Multiple reports can be specified as an array. + * + * @param mixed $report + * @return void + */ + public function addReport($report) { + + if (!is_array($report)) $report = array($report); + + foreach($report as $r) { + + if (!preg_match('/^{([^}]*)}(.*)$/',$r)) + throw new Sabre_DAV_Exception('Reportname must be in clark-notation'); + + $this->reports[] = $r; + + } + + } + + /** + * Returns the list of supported reports + * + * @return array + */ + public function getValue() { + + return $this->reports; + + } + + /** + * Serializes the node + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + foreach($this->reports as $reportName) { + + $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); + $prop->appendChild($supportedReport); + + $report = $prop->ownerDocument->createElement('d:report'); + $supportedReport->appendChild($report); + + preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); + + list(, $namespace, $element) = $matches; + + $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; + + if ($prefix) { + $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element)); + } else { + $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element)); + } + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Server.php b/3.0/modules/webdav/libraries/Sabre/DAV/Server.php new file mode 100755 index 00000000..21a61256 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Server.php @@ -0,0 +1,1821 @@ + 'd', + 'http://sabredav.org/ns' => 's', + ); + + /** + * The propertymap can be used to map properties from + * requests to property classes. + * + * @var array + */ + public $propertyMap = array( + ); + + public $protectedProperties = array( + // RFC4918 + '{DAV:}getcontentlength', + '{DAV:}getetag', + '{DAV:}getlastmodified', + '{DAV:}lockdiscovery', + '{DAV:}resourcetype', + '{DAV:}supportedlock', + + // RFC4331 + '{DAV:}quota-available-bytes', + '{DAV:}quota-used-bytes', + + // RFC3744 + '{DAV:}alternate-URI-set', + '{DAV:}principal-URL', + '{DAV:}group-membership', + '{DAV:}supported-privilege-set', + '{DAV:}current-user-privilege-set', + '{DAV:}acl', + '{DAV:}acl-restrictions', + '{DAV:}inherited-acl-set', + '{DAV:}principal-collection-set', + + // RFC5397 + '{DAV:}current-user-principal', + ); + + /** + * This is a flag that allow or not showing file, line and code + * of the exception in the returned XML + * + * @var bool + */ + public $debugExceptions = false; + + + /** + * Sets up the server + * + * If a Sabre_DAV_Tree object is passed as an argument, it will + * use it as the directory tree. If a Sabre_DAV_INode is passed, it + * will create a Sabre_DAV_ObjectTree and use the node as the root. + * + * If nothing is passed, a Sabre_DAV_SimpleDirectory is created in + * a Sabre_DAV_ObjectTree. + * + * @param Sabre_DAV_Tree $tree The tree object + * @return void + */ + public function __construct($treeOrNode = null) { + + if ($treeOrNode instanceof Sabre_DAV_Tree) { + $this->tree = $treeOrNode; + } elseif ($treeOrNode instanceof Sabre_DAV_INode) { + $this->tree = new Sabre_DAV_ObjectTree($treeOrNode); + } elseif (is_null($treeOrNode)) { + $root = new Sabre_DAV_SimpleDirectory('root'); + $this->tree = new Sabre_DAV_ObjectTree($root); + } else { + throw new Sabre_DAV_Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre_DAV_Tree, Sabre_DAV_INode or null'); + } + $this->httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + /** + * Starts the DAV Server + * + * @return void + */ + public function exec() { + + try { + + $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri()); + + } catch (Exception $e) { + + $DOM = new DOMDocument('1.0','utf-8'); + $DOM->formatOutput = true; + + $error = $DOM->createElementNS('DAV:','d:error'); + $error->setAttribute('xmlns:s',self::NS_SABREDAV); + $DOM->appendChild($error); + + $error->appendChild($DOM->createElement('s:exception',get_class($e))); + $error->appendChild($DOM->createElement('s:message',$e->getMessage())); + if ($this->debugExceptions) { + $error->appendChild($DOM->createElement('s:file',$e->getFile())); + $error->appendChild($DOM->createElement('s:line',$e->getLine())); + $error->appendChild($DOM->createElement('s:code',$e->getCode())); + $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString())); + + } + $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION)); + + if($e instanceof Sabre_DAV_Exception) { + + $httpCode = $e->getHTTPCode(); + $e->serialize($this,$error); + $headers = $e->getHTTPHeaders($this); + + } else { + + $httpCode = 500; + $headers = array(); + + } + $headers['Content-Type'] = 'application/xml; charset=utf-8'; + + $this->httpResponse->sendStatus($httpCode); + $this->httpResponse->setHeaders($headers); + $this->httpResponse->sendBody($DOM->saveXML()); + + } + + } + + /** + * Sets the base server uri + * + * @param string $uri + * @return void + */ + public function setBaseUri($uri) { + + // If the baseUri does not end with a slash, we must add it + if ($uri[strlen($uri)-1]!=='/') + $uri.='/'; + + $this->baseUri = $uri; + + } + + /** + * Returns the base responding uri + * + * @return string + */ + public function getBaseUri() { + + if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); + return $this->baseUri; + + } + + /** + * This method attempts to detect the base uri. + * Only the PATH_INFO variable is considered. + * + * If this variable is not set, the root (/) is assumed. + * + * @return void + */ + public function guessBaseUri() { + + $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); + $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); + + // If PATH_INFO is not found, we just return / + if (!empty($pathInfo)) { + + // We need to make sure we ignore the QUERY_STRING part + if ($pos = strpos($uri,'?')) + $uri = substr($uri,0,$pos); + + // PATH_INFO is only set for urls, such as: /example.php/path + // in that case PATH_INFO contains '/path'. + // Note that REQUEST_URI is percent encoded, while PATH_INFO is + // not, Therefore they are only comparable if we first decode + // REQUEST_INFO as well. + $decodedUri = Sabre_DAV_URLUtil::decodePath($uri); + + // A simple sanity check: + if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) { + $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo)); + return rtrim($baseUri,'/') . '/'; + } + + throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); + + } + + // If the url ended with .php, we're going to assume that that's the server root + if (strpos($uri,'.php')===strlen($uri)-4) { + return $uri . '/'; + } + + // The last fallback is that we're just going to assume the server root. + return '/'; + + } + + /** + * Adds a plugin to the server + * + * For more information, console the documentation of Sabre_DAV_ServerPlugin + * + * @param Sabre_DAV_ServerPlugin $plugin + * @return void + */ + public function addPlugin(Sabre_DAV_ServerPlugin $plugin) { + + $this->plugins[get_class($plugin)] = $plugin; + $plugin->initialize($this); + + } + + /** + * Returns an initialized plugin by it's classname. + * + * This function returns null if the plugin was not found. + * + * @param string $className + * @return Sabre_DAV_ServerPlugin + */ + public function getPlugin($className) { + + if (isset($this->plugins[$className])) return $this->plugins[$className]; + return null; + + } + + /** + * Subscribe to an event. + * + * When the event is triggered, we'll call all the specified callbacks. + * It is possible to control the order of the callbacks through the + * priority argument. + * + * This is for example used to make sure that the authentication plugin + * is triggered before anything else. If it's not needed to change this + * number, it is recommended to ommit. + * + * @param string $event + * @param callback $callback + * @param int $priority + * @return void + */ + public function subscribeEvent($event, $callback, $priority = 100) { + + if (!isset($this->eventSubscriptions[$event])) { + $this->eventSubscriptions[$event] = array(); + } + while(isset($this->eventSubscriptions[$event][$priority])) $priority++; + $this->eventSubscriptions[$event][$priority] = $callback; + ksort($this->eventSubscriptions[$event]); + + } + + /** + * Broadcasts an event + * + * This method will call all subscribers. If one of the subscribers returns false, the process stops. + * + * The arguments parameter will be sent to all subscribers + * + * @param string $eventName + * @param array $arguments + * @return bool + */ + public function broadcastEvent($eventName,$arguments = array()) { + + if (isset($this->eventSubscriptions[$eventName])) { + + foreach($this->eventSubscriptions[$eventName] as $subscriber) { + + $result = call_user_func_array($subscriber,$arguments); + if ($result===false) return false; + + } + + } + + return true; + + } + + /** + * Handles a http request, and execute a method based on its name + * + * @param string $method + * @param string $uri + * @return void + */ + public function invokeMethod($method, $uri) { + + $method = strtoupper($method); + + if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return; + + // Make sure this is a HTTP method we support + $internalMethods = array( + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'MKCOL', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ); + + if (in_array($method,$internalMethods)) { + + call_user_func(array($this,'http' . $method), $uri); + + } else { + + if ($this->broadcastEvent('unknownMethod',array($method, $uri))) { + // Unsupported method + throw new Sabre_DAV_Exception_NotImplemented(); + } + + } + + } + + // {{{ HTTP Method implementations + + /** + * HTTP OPTIONS + * + * @param string $uri + * @return void + */ + protected function httpOptions($uri) { + + $methods = $this->getAllowedMethods($uri); + + $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods))); + $features = array('1','3', 'extended-mkcol'); + + foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); + + $this->httpResponse->setHeader('DAV',implode(', ',$features)); + $this->httpResponse->setHeader('MS-Author-Via','DAV'); + $this->httpResponse->setHeader('Accept-Ranges','bytes'); + $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION); + $this->httpResponse->setHeader('Content-Length',0); + $this->httpResponse->sendStatus(200); + + } + + /** + * HTTP GET + * + * This method simply fetches the contents of a uri, like normal + * + * @param string $uri + * @return void + */ + protected function httpGet($uri) { + + $node = $this->tree->getNodeForPath($uri,0); + + if (!$this->checkPreconditions(true)) return false; + + if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); + $body = $node->get(); + + // Converting string into stream, if needed. + if (is_string($body)) { + $stream = fopen('php://temp','r+'); + fwrite($stream,$body); + rewind($stream); + $body = $stream; + } + + /* + * TODO: getetag, getlastmodified, getsize should also be used using + * this method + */ + $httpHeaders = $this->getHTTPHeaders($uri); + + /* ContentType needs to get a default, because many webservers will otherwise + * default to text/html, and we don't want this for security reasons. + */ + if (!isset($httpHeaders['Content-Type'])) { + $httpHeaders['Content-Type'] = 'application/octet-stream'; + } + + + if (isset($httpHeaders['Content-Length'])) { + + $nodeSize = $httpHeaders['Content-Length']; + + // Need to unset Content-Length, because we'll handle that during figuring out the range + unset($httpHeaders['Content-Length']); + + } else { + $nodeSize = null; + } + + $this->httpResponse->setHeaders($httpHeaders); + + $range = $this->getHTTPRange(); + $ifRange = $this->httpRequest->getHeader('If-Range'); + $ignoreRangeHeader = false; + + // If ifRange is set, and range is specified, we first need to check + // the precondition. + if ($nodeSize && $range && $ifRange) { + + // if IfRange is parsable as a date we'll treat it as a DateTime + // otherwise, we must treat it as an etag. + try { + $ifRangeDate = new DateTime($ifRange); + + // It's a date. We must check if the entity is modified since + // the specified date. + if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; + else { + $modified = new DateTime($httpHeaders['Last-Modified']); + if($modified > $ifRangeDate) $ignoreRangeHeader = true; + } + + } catch (Exception $e) { + + // It's an entity. We can do a simple comparison. + if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; + elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; + } + } + + // We're only going to support HTTP ranges if the backend provided a filesize + if (!$ignoreRangeHeader && $nodeSize && $range) { + + // Determining the exact byte offsets + if (!is_null($range[0])) { + + $start = $range[0]; + $end = $range[1]?$range[1]:$nodeSize-1; + if($start >= $nodeSize) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); + + if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); + if($end >= $nodeSize) $end = $nodeSize-1; + + } else { + + $start = $nodeSize-$range[1]; + $end = $nodeSize-1; + + if ($start<0) $start = 0; + + } + + // New read/write stream + $newStream = fopen('php://temp','r+'); + + stream_copy_to_stream($body, $newStream, $end-$start+1, $start); + rewind($newStream); + + $this->httpResponse->setHeader('Content-Length', $end-$start+1); + $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize); + $this->httpResponse->sendStatus(206); + $this->httpResponse->sendBody($newStream); + + + } else { + + if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize); + $this->httpResponse->sendStatus(200); + $this->httpResponse->sendBody($body); + + } + + } + + /** + * HTTP HEAD + * + * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body + * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again + * + * @param string $uri + * @return void + */ + protected function httpHead($uri) { + + $node = $this->tree->getNodeForPath($uri); + /* This information is only collection for File objects. + * Ideally we want to throw 405 Method Not Allowed for every + * non-file, but MS Office does not like this + */ + if ($node instanceof Sabre_DAV_IFile) { + $headers = $this->getHTTPHeaders($this->getRequestUri()); + if (!isset($headers['Content-Type'])) { + $headers['Content-Type'] = 'application/octet-stream'; + } + $this->httpResponse->setHeaders($headers); + } + $this->httpResponse->sendStatus(200); + + } + + /** + * HTTP Delete + * + * The HTTP delete method, deletes a given uri + * + * @param string $uri + * @return void + */ + protected function httpDelete($uri) { + + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; + $this->tree->delete($uri); + + $this->httpResponse->sendStatus(204); + $this->httpResponse->setHeader('Content-Length','0'); + + } + + + /** + * WebDAV PROPFIND + * + * This WebDAV method requests information about an uri resource, or a list of resources + * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value + * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) + * + * The request body contains an XML data structure that has a list of properties the client understands + * The response body is also an xml document, containing information about every uri resource and the requested properties + * + * It has to return a HTTP 207 Multi-status status code + * + * @param string $uri + * @return void + */ + protected function httpPropfind($uri) { + + // $xml = new Sabre_DAV_XMLReader(file_get_contents('php://input')); + $requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true)); + + $depth = $this->getHTTPDepth(1); + // The only two options for the depth of a propfind is 0 or 1 + if ($depth!=0) $depth = 1; + + $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); + + // This is a multi-status response + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $data = $this->generateMultiStatus($newProperties); + $this->httpResponse->sendBody($data); + + } + + /** + * WebDAV PROPPATCH + * + * This method is called to update properties on a Node. The request is an XML body with all the mutations. + * In this XML body it is specified which properties should be set/updated and/or deleted + * + * @param string $uri + * @return void + */ + protected function httpPropPatch($uri) { + + $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true)); + + $result = $this->updateProperties($uri, $newProperties); + + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->httpResponse->sendBody( + $this->generateMultiStatus(array($result)) + ); + + } + + /** + * HTTP PUT method + * + * This HTTP method updates a file, or creates a new one. + * + * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 200 Ok + * + * @param string $uri + * @return void + */ + protected function httpPut($uri) { + + $body = $this->httpRequest->getBody(); + + // Intercepting the Finder problem + if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) { + + /** + Many webservers will not cooperate well with Finder PUT requests, + because it uses 'Chunked' transfer encoding for the request body. + + The symptom of this problem is that Finder sends files to the + server, but they arrive as 0-lenght files in PHP. + + If we don't do anything, the user might think they are uploading + files successfully, but they end up empty on the server. Instead, + we throw back an error if we detect this. + + The reason Finder uses Chunked, is because it thinks the files + might change as it's being uploaded, and therefore the + Content-Length can vary. + + Instead it sends the X-Expected-Entity-Length header with the size + of the file at the very start of the request. If this header is set, + but we don't get a request body we will fail the request to + protect the end-user. + */ + + // Only reading first byte + $firstByte = fread($body,1); + if (strlen($firstByte)!==1) { + throw new Sabre_DAV_Exception_Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); + } + + // The body needs to stay intact, so we copy everything to a + // temporary stream. + + $newBody = fopen('php://temp','r+'); + fwrite($newBody,$firstByte); + stream_copy_to_stream($body, $newBody); + rewind($newBody); + + $body = $newBody; + + } + + if ($this->tree->nodeExists($uri)) { + + $node = $this->tree->getNodeForPath($uri); + + // Checking If-None-Match and related headers. + if (!$this->checkPreconditions()) return; + + // If the node is a collection, we'll deny it + if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.'); + if (!$this->broadcastEvent('beforeWriteContent',array($this->getRequestUri()))) return false; + + $node->put($body); + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(200); + + } else { + + // If we got here, the resource didn't exist yet. + $this->createFile($this->getRequestUri(),$body); + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(201); + + } + + } + + + /** + * WebDAV MKCOL + * + * The MKCOL method is used to create a new collection (directory) on the server + * + * @param string $uri + * @return void + */ + protected function httpMkcol($uri) { + + $requestBody = $this->httpRequest->getBody(true); + + if ($requestBody) { + + $contentType = $this->httpRequest->getHeader('Content-Type'); + if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) { + + // We must throw 415 for unsupport mkcol bodies + throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); + + } + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody); + if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { + + // We must throw 415 for unsupport mkcol bodies + throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); + + } + + $properties = array(); + foreach($dom->firstChild->childNodes as $childNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue; + $properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap)); + + } + if (!isset($properties['{DAV:}resourcetype'])) + throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property'); + + unset($properties['{DAV:}resourcetype']); + + $resourceType = array(); + // Need to parse out all the resourcetypes + $rtNode = $dom->firstChild->getElementsByTagNameNS('urn:DAV','resourcetype'); + $rtNode = $rtNode->item(0); + foreach($rtNode->childNodes as $childNode) {; + $resourceType[] = Sabre_DAV_XMLUtil::toClarkNotation($childNode); + } + + } else { + + $properties = array(); + $resourceType = array('{DAV:}collection'); + + } + + $result = $this->createCollection($uri, $resourceType, $properties); + + if (is_array($result)) { + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->httpResponse->sendBody( + $this->generateMultiStatus(array($result)) + ); + + } else { + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(201); + } + + } + + /** + * WebDAV HTTP MOVE method + * + * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo + * + * @param string $uri + * @return void + */ + protected function httpMove($uri) { + + $moveInfo = $this->getCopyAndMoveInfo(); + if ($moveInfo['destinationExists']) { + + if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false; + $this->tree->delete($moveInfo['destination']); + + } + + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false; + if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false; + $this->tree->move($uri,$moveInfo['destination']); + $this->broadcastEvent('afterBind',array($moveInfo['destination'])); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201); + + } + + /** + * WebDAV HTTP COPY method + * + * This method copies one uri to a different uri, and works much like the MOVE request + * A lot of the actual request processing is done in getCopyMoveInfo + * + * @param string $uri + * @return void + */ + protected function httpCopy($uri) { + + $copyInfo = $this->getCopyAndMoveInfo(); + if ($copyInfo['destinationExists']) { + + if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false; + $this->tree->delete($copyInfo['destination']); + + } + if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false; + $this->tree->copy($uri,$copyInfo['destination']); + $this->broadcastEvent('afterBind',array($copyInfo['destination'])); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201); + + } + + + + /** + * HTTP REPORT method implementation + * + * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) + * It's used in a lot of extensions, so it made sense to implement it into the core. + * + * @param string $uri + * @return void + */ + protected function httpReport($uri) { + + $body = $this->httpRequest->getBody(true); + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $reportName = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild); + + if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) { + + // If broadcastEvent returned true, it means the report was not supported + throw new Sabre_DAV_Exception_ReportNotImplemented(); + + } + + } + + // }}} + // {{{ HTTP/WebDAV protocol helpers + + /** + * Returns an array with all the supported HTTP methods for a specific uri. + * + * @param string $uri + * @return array + */ + public function getAllowedMethods($uri) { + + $methods = array( + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ); + + // The MKCOL is only allowed on an unmapped uri + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + $methods[] = 'MKCOL'; + } + + // We're also checking if any of the plugins register any new methods + foreach($this->plugins as $plugin) $methods = array_merge($methods,$plugin->getHTTPMethods($uri)); + array_unique($methods); + + return $methods; + + } + + /** + * Gets the uri for the request, keeping the base uri into consideration + * + * @return string + */ + public function getRequestUri() { + + return $this->calculateUri($this->httpRequest->getUri()); + + } + + /** + * Calculates the uri for a request, making sure that the base uri is stripped out + * + * @param string $uri + * @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri + * @return string + */ + public function calculateUri($uri) { + + if ($uri[0]!='/' && strpos($uri,'://')) { + + $uri = parse_url($uri,PHP_URL_PATH); + + } + + $uri = str_replace('//','/',$uri); + + if (strpos($uri,$this->getBaseUri())===0) { + + return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); + + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + } elseif ($uri.'/' === $this->getBaseUri()) { + + return ''; + + } else { + + throw new Sabre_DAV_Exception_Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); + + } + + } + + /** + * Returns the HTTP depth header + * + * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object + * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existant + * + * @param mixed $default + * @return int + */ + public function getHTTPDepth($default = self::DEPTH_INFINITY) { + + // If its not set, we'll grab the default + $depth = $this->httpRequest->getHeader('Depth'); + if (is_null($depth)) return $default; + + if ($depth == 'infinity') return self::DEPTH_INFINITY; + + // If its an unknown value. we'll grab the default + if (!ctype_digit($depth)) return $default; + + return (int)$depth; + + } + + /** + * Returns the HTTP range header + * + * This method returns null if there is no well-formed HTTP range request + * header or array($start, $end). + * + * The first number is the offset of the first byte in the range. + * The second number is the offset of the last byte in the range. + * + * If the second offset is null, it should be treated as the offset of the last byte of the entity + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * + * return $mixed + */ + public function getHTTPRange() { + + $range = $this->httpRequest->getHeader('range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; + + if ($matches[1]==='' && $matches[2]==='') return null; + + return array( + $matches[1]!==''?$matches[1]:null, + $matches[2]!==''?$matches[2]:null, + ); + + } + + + /** + * Returns information about Copy and Move requests + * + * This function is created to help getting information about the source and the destination for the + * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions + * + * The returned value is an array with the following keys: + * * destination - Destination path + * * destinationExists - Wether or not the destination is an existing url (and should therefore be overwritten) + * + * @return array + */ + public function getCopyAndMoveInfo() { + + // Collecting the relevant HTTP headers + if (!$this->httpRequest->getHeader('Destination')) throw new Sabre_DAV_Exception_BadRequest('The destination header was not supplied'); + $destination = $this->calculateUri($this->httpRequest->getHeader('Destination')); + $overwrite = $this->httpRequest->getHeader('Overwrite'); + if (!$overwrite) $overwrite = 'T'; + if (strtoupper($overwrite)=='T') $overwrite = true; + elseif (strtoupper($overwrite)=='F') $overwrite = false; + // We need to throw a bad request exception, if the header was invalid + else throw new Sabre_DAV_Exception_BadRequest('The HTTP Overwrite header should be either T or F'); + + list($destinationDir) = Sabre_DAV_URLUtil::splitPath($destination); + + try { + $destinationParent = $this->tree->getNodeForPath($destinationDir); + if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection'); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + // If the destination parent node is not found, we throw a 409 + throw new Sabre_DAV_Exception_Conflict('The destination node is not found'); + } + + try { + + $destinationNode = $this->tree->getNodeForPath($destination); + + // If this succeeded, it means the destination already exists + // we'll need to throw precondition failed in case overwrite is false + if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + // Destination didn't exist, we're all good + $destinationNode = false; + + + + } + + // These are the three relevant properties we need to return + return array( + 'destination' => $destination, + 'destinationExists' => $destinationNode==true, + 'destinationNode' => $destinationNode, + ); + + } + + /** + * Returns a list of properties for a path + * + * This is a simplified version getPropertiesForPath. + * if you aren't interested in status codes, but you just + * want to have a flat list of properties. Use this method. + * + * @param string $path + * @param array $propertyNames + */ + public function getProperties($path, $propertyNames) { + + $result = $this->getPropertiesForPath($path,$propertyNames,0); + return $result[0][200]; + + } + + /** + * Returns a list of HTTP headers for a particular resource + * + * The generated http headers are based on properties provided by the + * resource. The method basically provides a simple mapping between + * DAV property and HTTP header. + * + * The headers are intended to be used for HEAD and GET requests. + * + * @param string $path + */ + public function getHTTPHeaders($path) { + + $propertyMap = array( + '{DAV:}getcontenttype' => 'Content-Type', + '{DAV:}getcontentlength' => 'Content-Length', + '{DAV:}getlastmodified' => 'Last-Modified', + '{DAV:}getetag' => 'ETag', + ); + + $properties = $this->getProperties($path,array_keys($propertyMap)); + + $headers = array(); + foreach($propertyMap as $property=>$header) { + if (!isset($properties[$property])) continue; + + if (is_scalar($properties[$property])) { + $headers[$header] = $properties[$property]; + + // GetLastModified gets special cased + } elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) { + $headers[$header] = $properties[$property]->getTime()->format(DateTime::RFC1123); + } + + } + + return $headers; + + } + + /** + * Returns a list of properties for a given path + * + * The path that should be supplied should have the baseUrl stripped out + * The list of properties should be supplied in Clark notation. If the list is empty + * 'allprops' is assumed. + * + * If a depth of 1 is requested child elements will also be returned. + * + * @param string $path + * @param array $propertyNames + * @param int $depth + * @return array + */ + public function getPropertiesForPath($path,$propertyNames = array(),$depth = 0) { + + if ($depth!=0) $depth = 1; + + $returnPropertyList = array(); + + $parentNode = $this->tree->getNodeForPath($path); + $nodes = array( + $path => $parentNode + ); + if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) { + foreach($this->tree->getChildren($path) as $childNode) + $nodes[$path . '/' . $childNode->getName()] = $childNode; + } + + // If the propertyNames array is empty, it means all properties are requested. + // We shouldn't actually return everything we know though, and only return a + // sensible list. + $allProperties = count($propertyNames)==0; + + foreach($nodes as $myPath=>$node) { + + $newProperties = array( + '200' => array(), + '404' => array(), + ); + if ($node instanceof Sabre_DAV_IProperties) + $newProperties['200'] = $node->getProperties($propertyNames); + + if ($allProperties) { + + // Default list of propertyNames, when all properties were requested. + $propertyNames = array( + '{DAV:}getlastmodified', + '{DAV:}getcontentlength', + '{DAV:}resourcetype', + '{DAV:}quota-used-bytes', + '{DAV:}quota-available-bytes', + '{DAV:}getetag', + '{DAV:}getcontenttype', + ); + + // We need to make sure this includes any propertyname already returned from + // $node->getProperties(); + $propertyNames = array_merge($propertyNames, array_keys($newProperties[200])); + + // Making sure there's no double entries + $propertyNames = array_unique($propertyNames); + + } + + // If the resourceType was not part of the list, we manually add it + // and mark it for removal. We need to know the resourcetype in order + // to make certain decisions about the entry. + // WebDAV dictates we should add a / and the end of href's for collections + $removeRT = false; + if (!in_array('{DAV:}resourcetype',$propertyNames)) { + $propertyNames[] = '{DAV:}resourcetype'; + $removeRT = true; + } + + foreach($propertyNames as $prop) { + + if (isset($newProperties[200][$prop])) continue; + + switch($prop) { + case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break; + case '{DAV:}getcontentlength' : if ($node instanceof Sabre_DAV_IFile) $newProperties[200][$prop] = (int)$node->getSize(); break; + case '{DAV:}resourcetype' : $newProperties[200][$prop] = new Sabre_DAV_Property_ResourceType($node instanceof Sabre_DAV_ICollection?self::NODE_DIRECTORY:self::NODE_FILE); break; + case '{DAV:}quota-used-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[0]; + } + break; + case '{DAV:}quota-available-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[1]; + } + break; + case '{DAV:}getetag' : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; + case '{DAV:}getcontenttype' : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; + case '{DAV:}supported-report-set' : $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet(); break; + + } + + // If we were unable to find the property, we will list it as 404. + if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; + + } + + $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties)); + + $newProperties['href'] = trim($myPath,'/'); + + // Its is a WebDAV recommendation to add a trailing slash to collectionnames. + // Apple's iCal also requires a trailing slash for principals (rfc 3744). + // Therefore we add a trailing / for any non-file. This might need adjustments + // if we find there are other edge cases. + if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && $newProperties[200]['{DAV:}resourcetype']->getValue()!==null) $newProperties['href'] .='/'; + + // If the resourcetype property was manually added to the requested property list, + // we will remove it again. + if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']); + + $returnPropertyList[] = $newProperties; + + } + + return $returnPropertyList; + + } + + /** + * This method is invoked by sub-systems creating a new file. + * + * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). + * It was important to get this done through a centralized function, + * allowing plugins to intercept this using the beforeCreateFile event. + * + * @param string $uri + * @param resource $data + * @return void + */ + public function createFile($uri,$data) { + + list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri); + + if (!$this->broadcastEvent('beforeBind',array($uri))) return; + if (!$this->broadcastEvent('beforeCreateFile',array($uri,$data))) return; + + $parent = $this->tree->getNodeForPath($dir); + $parent->createFile($name,$data); + $this->tree->markDirty($dir); + + $this->broadcastEvent('afterBind',array($uri)); + } + + /** + * This method is invoked by sub-systems creating a new directory. + * + * @param string $uri + * @return void + */ + public function createDirectory($uri) { + + $this->createCollection($uri,array('{DAV:}collection'),array()); + + } + + /** + * Use this method to create a new collection + * + * The {DAV:}resourcetype is specified using the resourceType array. + * At the very least it must contain {DAV:}collection. + * + * The properties array can contain a list of additional properties. + * + * @param string $uri The new uri + * @param array $resourceType The resourceType(s) + * @param array $properties A list of properties + * @return void + */ + public function createCollection($uri, array $resourceType, array $properties) { + + list($parentUri,$newName) = Sabre_DAV_URLUtil::splitPath($uri); + + // Making sure {DAV:}collection was specified as resourceType + if (!in_array('{DAV:}collection', $resourceType)) { + throw new Sabre_DAV_Exception_InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection'); + } + + + // Making sure the parent exists + try { + + $parent = $this->tree->getNodeForPath($parentUri); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + throw new Sabre_DAV_Exception_Conflict('Parent node does not exist'); + + } + + // Making sure the parent is a collection + if (!$parent instanceof Sabre_DAV_ICollection) { + throw new Sabre_DAV_Exception_Conflict('Parent node is not a collection'); + } + + + + // Making sure the child does not already exist + try { + $parent->getChild($newName); + + // If we got here.. it means there's already a node on that url, and we need to throw a 405 + throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists'); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + // This is correct + } + + + if (!$this->broadcastEvent('beforeBind',array($uri))) return; + + // There are 2 modes of operation. The standard collection + // creates the directory, and then updates properties + // the extended collection can create it directly. + if ($parent instanceof Sabre_DAV_IExtendedCollection) { + + $parent->createExtendedCollection($newName, $resourceType, $properties); + + } else { + + // No special resourcetypes are supported + if (count($resourceType)>1) { + throw new Sabre_DAV_Exception_InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); + } + + $parent->createDirectory($newName); + $rollBack = false; + $exception = null; + $errorResult = null; + + if (count($properties)>0) { + + try { + + $errorResult = $this->updateProperties($uri, $properties); + if (!isset($errorResult[200])) { + $rollBack = true; + } + + } catch (Sabre_DAV_Exception $e) { + + $rollBack = true; + $exception = $e; + + } + + } + + if ($rollBack) { + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; + $this->tree->delete($uri); + + // Re-throwing exception + if ($exception) throw $exception; + + return $errorResult; + } + + } + $this->tree->markDirty($parentUri); + $this->broadcastEvent('afterBind',array($uri)); + + } + + /** + * This method updates a resource's properties + * + * The properties array must be a list of properties. Array-keys are + * property names in clarknotation, array-values are it's values. + * If a property must be deleted, the value should be null. + * + * Note that this request should either completely succeed, or + * completely fail. + * + * The response is an array with statuscodes for keys, which in turn + * contain arrays with propertynames. This response can be used + * to generate a multistatus body. + * + * @param string $uri + * @param array $properties + * @return array + */ + public function updateProperties($uri, array $properties) { + + // we'll start by grabbing the node, this will throw the appropriate + // exceptions if it doesn't. + $node = $this->tree->getNodeForPath($uri); + + $result = array( + 200 => array(), + 403 => array(), + 424 => array(), + ); + $remainingProperties = $properties; + $hasError = false; + + + // If the node is not an instance of Sabre_DAV_IProperties, every + // property is 403 Forbidden + // simply return a 405. + if (!($node instanceof Sabre_DAV_IProperties)) { + $hasError = true; + foreach($properties as $propertyName=> $value) { + $result[403][$propertyName] = null; + } + $remainingProperties = array(); + } + + // Running through all properties to make sure none of them are protected + if (!$hasError) foreach($properties as $propertyName => $value) { + if(in_array($propertyName, $this->protectedProperties)) { + $result[403][$propertyName] = null; + unset($remainingProperties[$propertyName]); + $hasError = true; + } + } + + // Only if there were no errors we may attempt to update the resource + if (!$hasError) { + $updateResult = $node->updateProperties($properties); + $remainingProperties = array(); + + if ($updateResult===true) { + // success + foreach($properties as $propertyName=>$value) { + $result[200][$propertyName] = null; + } + + } elseif ($updateResult===false) { + // The node failed to update the properties for an + // unknown reason + foreach($properties as $propertyName=>$value) { + $result[403][$propertyName] = null; + } + + } elseif (is_array($updateResult)) { + // The node has detailed update information + $result = $updateResult; + + } else { + throw new Sabre_DAV_Exception('Invalid result from updateProperties'); + } + + } + + foreach($remainingProperties as $propertyName=>$value) { + // if there are remaining properties, it must mean + // there's a dependency failure + $result[424][$propertyName] = null; + } + + // Removing empty array values + foreach($result as $status=>$props) { + + if (count($props)===0) unset($result[$status]); + + } + $result['href'] = $uri; + return $result; + + } + + /** + * This method checks the main HTTP preconditions. + * + * Currently these are: + * * If-Match + * * If-None-Match + * * If-Modified-Since + * * If-Unmodified-Since + * + * The method will return true if all preconditions are met + * The method will return false, or throw an exception if preconditions + * failed. If false is returned the operation should be aborted, and + * the appropriate HTTP response headers are already set. + * + * Normally this method will throw 412 Precondition Failed for failures + * related to If-None-Match, If-Match and If-Unmodified Since. It will + * set the status to 304 Not Modified for If-Modified_since. + * + * If the $handleAsGET argument is set to true, it will also return 304 + * Not Modified for failure of the If-None-Match precondition. This is the + * desired behaviour for HTTP GET and HTTP HEAD requests. + * + * @return bool + */ + public function checkPreconditions($handleAsGET = false) { + + $uri = $this->getRequestUri(); + $node = null; + $lastMod = null; + $etag = null; + + if ($ifMatch = $this->httpRequest->getHeader('If-Match')) { + + // If-Match contains an entity tag. Only if the entity-tag + // matches we are allowed to make the request succeed. + // If the entity-tag is '*' we are only allowed to make the + // request succeed if a resource exists at that url. + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); + } + + // Only need to check entity tags if they are not * + if ($ifMatch!=='*') { + + // There can be multiple etags + $ifMatch = explode(',',$ifMatch); + $haveMatch = false; + foreach($ifMatch as $ifMatchItem) { + + // Stripping any extra spaces + $ifMatchItem = trim($ifMatchItem,' '); + + $etag = $node->getETag(); + if ($etag===$ifMatchItem) { + $haveMatch = true; + } + } + if (!$haveMatch) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); + } + } + } + + if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) { + + // The If-None-Match header contains an etag. + // Only if the ETag does not match the current ETag, the request will succeed + // The header can also contain *, in which case the request + // will only succeed if the entity does not exist at all. + $nodeExists = true; + if (!$node) { + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + $nodeExists = false; + } + } + if ($nodeExists) { + $haveMatch = false; + if ($ifNoneMatch==='*') $haveMatch = true; + else { + + // There might be multiple etags + $ifNoneMatch = explode(',', $ifNoneMatch); + $etag = $node->getETag(); + + foreach($ifNoneMatch as $ifNoneMatchItem) { + + // Stripping any extra spaces + $ifNoneMatchItem = trim($ifNoneMatchItem,' '); + + if ($etag===$ifNoneMatchItem) $haveMatch = true; + + } + + } + + if ($haveMatch) { + if ($handleAsGET) { + $this->httpResponse->sendStatus(304); + return false; + } else { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match'); + } + } + } + + } + + if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) { + + // The If-Modified-Since header contains a date. We + // will only return the entity if it has been changed since + // that date. If it hasn't been changed, we return a 304 + // header + // Note that this header only has to be checked if there was no If-None-Match header + // as per the HTTP spec. + $date = Sabre_HTTP_Util::parseHTTPDate($ifModifiedSince); + + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($uri); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new DateTime('@' . $lastMod); + if ($lastMod <= $date) { + $this->httpResponse->sendStatus(304); + return false; + } + } + } + } + + if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) { + + // The If-Unmodified-Since will allow allow the request if the + // entity has not changed since the specified date. + $date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince); + + // We must only check the date if it's valid + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($uri); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new DateTime('@' . $lastMod); + if ($lastMod > $date) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since'); + } + } + } + + } + return true; + + } + + // }}} + // {{{ XML Readers & Writers + + + /** + * Generates a WebDAV propfind response body based on a list of nodes + * + * @param array $fileProperties The list with nodes + * @param array $requestedProperties The properties that should be returned + * @return string + */ + public function generateMultiStatus(array $fileProperties) { + + $dom = new DOMDocument('1.0','utf-8'); + //$dom->formatOutput = true; + $multiStatus = $dom->createElement('d:multistatus'); + $dom->appendChild($multiStatus); + + // Adding in default namespaces + foreach($this->xmlNamespaces as $namespace=>$prefix) { + + $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); + + } + + foreach($fileProperties as $entry) { + + $href = $entry['href']; + unset($entry['href']); + + $response = new Sabre_DAV_Property_Response($href,$entry); + $response->serialize($this,$multiStatus); + + } + + return $dom->saveXML(); + + } + + /** + * This method parses a PropPatch request + * + * PropPatch changes the properties for a resource. This method + * returns a list of properties. + * + * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, + * and the value contains the property value. If a property is to be removed the value + * will be null. + * + * @param string $body xml body + * @return array list of properties in need of updating or deletion + */ + public function parsePropPatchRequest($body) { + + //We'll need to change the DAV namespace declaration to something else in order to make it parsable + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $newProperties = array(); + + foreach($dom->firstChild->childNodes as $child) { + + if ($child->nodeType !== XML_ELEMENT_NODE) continue; + + $operation = Sabre_DAV_XMLUtil::toClarkNotation($child); + + if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; + + $innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap); + + foreach($innerProperties as $propertyName=>$propertyValue) { + + if ($operation==='{DAV:}remove') { + $propertyValue = null; + } + + $newProperties[$propertyName] = $propertyValue; + + } + + } + + return $newProperties; + + } + + /** + * This method parses the PROPFIND request and returns its information + * + * This will either be a list of properties, or an empty array; in which case + * an {DAV:}allprop was requested. + * + * @param string $body + * @return array + */ + public function parsePropFindRequest($body) { + + // If the propfind body was empty, it means IE is requesting 'all' properties + if (!$body) return array(); + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); + return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); + + } + + // }}} + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php new file mode 100755 index 00000000..f38e8aa3 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php @@ -0,0 +1,60 @@ +name = $name; + foreach($children as $child) { + + if (!($child instanceof Sabre_DAV_INode)) throw new Sabre_DAV_Exception('Only instances of Sabre_DAV_INode are allowed to be passed in the children argument'); + $this->addChild($child); + + } + + } + + /** + * Adds a new childnode to this collection + * + * @param Sabre_DAV_INode $child + * @return void + */ + public function addChild(Sabre_DAV_INode $child) { + + $this->children[$child->getName()] = $child; + + } + + /** + * Returns the name of the collection + * + * @return string + */ + public function getName() { + + return $this->name; + + } + + /** + * Returns a child object, by its name. + * + * This method makes use of the getChildren method to grab all the child nodes, and compares the name. + * Generally its wise to override this, as this can usually be optimized + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + if (isset($this->children[$name])) return $this->children[$name]; + throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name); + + } + + /** + * Returns a list of children for this collection + * + * @return array + */ + public function getChildren() { + + return array_values($this->children); + + } + + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php new file mode 100755 index 00000000..1e15865e --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php @@ -0,0 +1,275 @@ +dataDir = $dataDir; + + } + + /** + * Initialize the plugin + * + * This is called automatically be the Server class after this plugin is + * added with Sabre_DAV_Server::addPlugin() + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + $this->server = $server; + $server->subscribeEvent('beforeMethod',array($this,'beforeMethod')); + $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile')); + + } + + /** + * This method is called before any HTTP method handler + * + * This method intercepts any GET, DELETE, PUT and PROPFIND calls to + * filenames that are known to match the 'temporary file' regex. + * + * @param string $method + * @return bool + */ + public function beforeMethod($method, $uri) { + + if (!$tempLocation = $this->isTempFile($uri)) + return true; + + switch($method) { + case 'GET' : + return $this->httpGet($tempLocation); + case 'PUT' : + return $this->httpPut($tempLocation); + case 'PROPFIND' : + return $this->httpPropfind($tempLocation, $uri); + case 'DELETE' : + return $this->httpDelete($tempLocation); + } + return true; + + } + + /** + * This method is invoked if some subsystem creates a new file. + * + * This is used to deal with HTTP LOCK requests which create a new + * file. + * + * @param string $uri + * @param resource $data + * @return bool + */ + public function beforeCreateFile($uri,$data) { + if ($tempPath = $this->isTempFile($uri)) { + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + file_put_contents($tempPath,$data); + return false; + } + return true; + + } + + /** + * This method will check if the url matches the temporary file pattern + * if it does, it will return an path based on $this->dataDir for the + * temporary file storage. + * + * @param string $path + * @return boolean|string + */ + protected function isTempFile($path) { + + // We're only interested in the basename. + list(, $tempPath) = Sabre_DAV_URLUtil::splitPath($path); + + foreach($this->temporaryFilePatterns as $tempFile) { + + if (preg_match($tempFile,$tempPath)) { + return $this->dataDir . '/sabredav_' . md5($path) . '.tempfile'; + } + + } + + return false; + + } + + + /** + * This method handles the GET method for temporary files. + * If the file doesn't exist, it will return false which will kick in + * the regular system for the GET method. + * + * @param string $tempLocation + * @return bool + */ + public function httpGet($tempLocation) { + + if (!file_exists($tempLocation)) return true; + + $hR = $this->server->httpResponse; + $hR->setHeader('Content-Type','application/octet-stream'); + $hR->setHeader('Content-Length',filesize($tempLocation)); + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(200); + $hR->sendBody(fopen($tempLocation,'r')); + return false; + + } + + /** + * This method handles the PUT method. + * + * @param string $tempLocation + * @return bool + */ + public function httpPut($tempLocation) { + error_log("Tempfile PUT"); + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + + $newFile = !file_exists($tempLocation); + + if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { + throw new Sabre_DAV_Exception_PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); + } + + file_put_contents($tempLocation,$this->server->httpRequest->getBody()); + $hR->sendStatus($newFile?201:200); + return false; + + } + + /** + * This method handles the DELETE method. + * + * If the file didn't exist, it will return false, which will make the + * standard HTTP DELETE handler kick in. + * + * @param string $tempLocation + * @return bool + */ + public function httpDelete($tempLocation) { + + if (!file_exists($tempLocation)) return true; + + unlink($tempLocation); + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(204); + return false; + + } + + /** + * This method handles the PROPFIND method. + * + * It's a very lazy method, it won't bother checking the request body + * for which properties were requested, and just sends back a default + * set of properties. + * + * @param string $tempLocation + * @return void + */ + public function httpPropfind($tempLocation, $uri) { + + if (!file_exists($tempLocation)) return true; + + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(207); + $hR->setHeader('Content-Type','application/xml; charset=utf-8'); + + $requestedProps = $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); + + $properties = array( + 'href' => $uri, + 200 => array( + '{DAV:}getlastmodified' => new Sabre_DAV_Property_GetLastModified(filemtime($tempLocation)), + '{DAV:}getcontentlength' => filesize($tempLocation), + '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType(null), + '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true, + + ), + ); + + $data = $this->server->generateMultiStatus(array($properties)); + $hR->sendBody($data); + return false; + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php b/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php new file mode 100755 index 00000000..f7191e23 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php @@ -0,0 +1,192 @@ +getNodeForPath($path); + return true; + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + return false; + + } + + } + + /** + * Copies a file from path to another + * + * @param string $sourcePath The source location + * @param string $destinationPath The full destination path + * @return void + */ + public function copy($sourcePath, $destinationPath) { + + $sourceNode = $this->getNodeForPath($sourcePath); + + // grab the dirname and basename components + list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); + + $destinationParent = $this->getNodeForPath($destinationDir); + $this->copyNode($sourceNode,$destinationParent,$destinationName); + + $this->markDirty($destinationDir); + + } + + /** + * Moves a file from one location to another + * + * @param string $sourcePath The path to the file which should be moved + * @param string $destinationPath The full destination path, so not just the destination parent node + * @return int + */ + public function move($sourcePath, $destinationPath) { + + list($sourceDir, $sourceName) = Sabre_DAV_URLUtil::splitPath($sourcePath); + list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); + + if ($sourceDir===$destinationDir) { + $renameable = $this->getNodeForPath($sourcePath); + $renameable->setName($destinationName); + } else { + $this->copy($sourcePath,$destinationPath); + $this->getNodeForPath($sourcePath)->delete(); + } + $this->markDirty($sourceDir); + $this->markDirty($destinationDir); + + } + + /** + * Deletes a node from the tree + * + * @param string $path + * @return void + */ + public function delete($path) { + + $node = $this->getNodeForPath($path); + $node->delete(); + + list($parent) = Sabre_DAV_URLUtil::splitPath($path); + $this->markDirty($parent); + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + public function getChildren($path) { + + $node = $this->getNodeForPath($path); + return $node->getChildren(); + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + public function markDirty($path) { + + + } + + /** + * copyNode + * + * @param Sabre_DAV_INode $source + * @param Sabre_DAV_ICollection $destination + * @return void + */ + protected function copyNode(Sabre_DAV_INode $source,Sabre_DAV_ICollection $destinationParent,$destinationName = null) { + + if (!$destinationName) $destinationName = $source->getName(); + + if ($source instanceof Sabre_DAV_IFile) { + + $data = $source->get(); + + // If the body was a string, we need to convert it to a stream + if (is_string($data)) { + $stream = fopen('php://temp','r+'); + fwrite($stream,$data); + rewind($stream); + $data = $stream; + } + $destinationParent->createFile($destinationName,$data); + $destination = $destinationParent->getChild($destinationName); + + } elseif ($source instanceof Sabre_DAV_ICollection) { + + $destinationParent->createDirectory($destinationName); + + $destination = $destinationParent->getChild($destinationName); + foreach($source->getChildren() as $child) { + + $this->copyNode($child,$destination); + + } + + } + if ($source instanceof Sabre_DAV_IProperties && $destination instanceof Sabre_DAV_IProperties) { + + $props = $source->getProperties(array()); + $destination->updateProperties($props); + + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php b/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php new file mode 100755 index 00000000..ee195eb2 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php @@ -0,0 +1,124 @@ +basePath = $basePath; + + } + + /** + * Returns a new node for the given path + * + * @param string $path + * @return void + */ + public function getNodeForPath($path) { + + $realPath = $this->getRealPath($path); + if (!file_exists($realPath)) throw new Sabre_DAV_Exception_FileNotFound('File at location ' . $realPath . ' not found'); + if (is_dir($realPath)) { + return new Sabre_DAV_FS_Directory($path); + } else { + return new Sabre_DAV_FS_File($path); + } + + } + + /** + * Returns the real filesystem path for a webdav url. + * + * @param string $publicPath + * @return string + */ + protected function getRealPath($publicPath) { + + return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/'); + + } + + /** + * Copies a file or directory. + * + * This method must work recursively and delete the destination + * if it exists + * + * @param string $source + * @param string $destination + * @return void + */ + public function copy($source,$destination) { + + $source = $this->getRealPath($source); + $destination = $this->getRealPath($destination); + $this->realCopy($source,$destination); + + } + + /** + * Used by self::copy + * + * @param string $source + * @param string $destination + * @return void + */ + protected function realCopy($source,$destination) { + + if (is_file($source)) { + copy($source,$destination); + } else { + mkdir($destination); + foreach(scandir($source) as $subnode) { + + if ($subnode=='.' || $subnode=='..') continue; + $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode); + + } + } + + } + + /** + * Moves a file or directory recursively. + * + * If the destination exists, delete it first. + * + * @param string $source + * @param string $destination + * @return void + */ + public function move($source,$destination) { + + $source = $this->getRealPath($source); + $destination = $this->getRealPath($destination); + rename($source,$destination); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php b/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php new file mode 100755 index 00000000..0dfc6896 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php @@ -0,0 +1,141 @@ +=0x41 /* A */ && $c<=0x5a /* Z */) || + ($c>=0x61 /* a */ && $c<=0x7a /* z */) || + ($c>=0x30 /* 0 */ && $c<=0x39 /* 9 */) || + $c===0x5f /* _ */ || + $c===0x2d /* - */ || + $c===0x2e /* . */ || + $c===0x7E /* ~ */ || + + /* Reserved, but no reserved purpose */ + $c===0x28 /* ( */ || + $c===0x29 /* ) */ + + ) { + $newStr.=$pathSegment[$i]; + } else { + $newStr.='%' . str_pad(dechex($c), 2, '0', STR_PAD_LEFT); + } + + } + return $newStr; + + } + + /** + * Decodes a url-encoded path + * + * @param string $path + * @return string + */ + static function decodePath($path) { + + return self::decodePathSegment($path); + + } + + /** + * Decodes a url-encoded path segment + * + * @param string $path + * @return string + */ + static function decodePathSegment($path) { + + $path = urldecode($path); + $encoding = mb_detect_encoding($path, array('UTF-8','ISO-8859-1')); + + switch($encoding) { + + case 'ISO-8859-1' : + $path = utf8_encode($path); + } + + return $path; + + } + + /** + * Returns the 'dirname' and 'basename' for a path. + * + * The reason there is a custom function for this purpose, is because + * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale is used) + * and we need a method that just operates on UTF-8 characters. + * + * In addition basename and dirname are platform aware, and will treat backslash (\) as a + * directory separator on windows. + * + * This method returns the 2 components as an array. + * + * If there is no dirname, it will return an empty string. Any / appearing at the end of the + * string is stripped off. + * + * @param string $path + * @return array + */ + static function splitPath($path) { + + $matches = array(); + if(preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u',$path,$matches)) { + return array($matches[1],$matches[2]); + } else { + return array(null,null); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Version.php b/3.0/modules/webdav/libraries/Sabre/DAV/Version.php new file mode 100755 index 00000000..20584782 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Version.php @@ -0,0 +1,24 @@ + + * will be returned as: + * {http://www.example.org}myelem + * + * This format is used throughout the SabreDAV sourcecode. + * Elements encoded with the urn:DAV namespace will + * be returned as if they were in the DAV: namespace. This is to avoid + * compatibility problems. + * + * This function will return null if a nodetype other than an Element is passed. + * + * @param DOMElement $dom + * @return string + */ + static function toClarkNotation(DOMNode $dom) { + + if ($dom->nodeType !== XML_ELEMENT_NODE) return null; + + // Mapping back to the real namespace, in case it was dav + if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; + + // Mapping to clark notation + return '{' . $ns . '}' . $dom->localName; + + } + + /** + * This method takes an XML document (as string) and converts all instances of the + * DAV: namespace to urn:DAV + * + * This is unfortunately needed, because the DAV: namespace violates the xml namespaces + * spec, and causes the DOM to throw errors + */ + static function convertDAVNamespace($xmlDocument) { + + // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: + // namespace is actually a violation of the XML namespaces specification, and will cause errors + return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); + + } + + /** + * This method provides a generic way to load a DOMDocument for WebDAV use. + * + * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors. + * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. + * + * @param string $xml + * @throws Sabre_DAV_Exception_BadRequest + * @return DOMDocument + */ + static function loadDOMDocument($xml) { + + if (empty($xml)) + throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent'); + + // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) + // does not support this, so we must intercept this and convert to UTF-8. + if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { + + // Note: the preceeding byte sequence is "]*)encoding="UTF-16"([^>]*)>|u','',$xml); + + } + + // Retaining old error setting + $oldErrorSetting = libxml_use_internal_errors(true); + + // Clearing any previous errors + libxml_clear_errors(); + + $dom = new DOMDocument(); + $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); + + // We don't generally care about any whitespace + $dom->preserveWhiteSpace = false; + + if ($error = libxml_get_last_error()) { + libxml_clear_errors(); + throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); + } + + // Restoring old mechanism for error handling + if ($oldErrorSetting===false) libxml_use_internal_errors(false); + + return $dom; + + } + + /** + * Parses all WebDAV properties out of a DOM Element + * + * Generally WebDAV properties are encloded in {DAV:}prop elements. This + * method helps by going through all these and pulling out the actual + * propertynames, making them array keys and making the property values, + * well.. the array values. + * + * If no value was given (self-closing element) null will be used as the + * value. This is used in for example PROPFIND requests. + * + * Complex values are supported through the propertyMap argument. The + * propertyMap should have the clark-notation properties as it's keys, and + * classnames as values. + * + * When any of these properties are found, the unserialize() method will be + * (statically) called. The result of this method is used as the value. + * + * @param DOMElement $parentNode + * @param array $propertyMap + * @return array + */ + static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) { + + $propList = array(); + foreach($parentNode->childNodes as $propNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue; + + foreach($propNode->childNodes as $propNodeData) { + + /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ + if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue; + + $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData); + if (isset($propertyMap[$propertyName])) { + $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); + } else { + $propList[$propertyName] = $propNodeData->textContent; + } + } + + + } + return $propList; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php new file mode 100755 index 00000000..b97fea9d --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php @@ -0,0 +1,226 @@ +httpRequest->getHeader('Authorization'); + $authHeader = explode(' ',$authHeader); + + if ($authHeader[0]!='AWS' || !isset($authHeader[1])) { + $this->errorCode = self::ERR_NOAWSHEADER; + return false; + } + + list($this->accessKey,$this->signature) = explode(':',$authHeader[1]); + + return true; + + } + + /** + * Returns the username for the request + * + * @return string + */ + public function getAccessKey() { + + return $this->accessKey; + + } + + /** + * Validates the signature based on the secretKey + * + * @return bool + */ + public function validate($secretKey) { + + $contentMD5 = $this->httpRequest->getHeader('Content-MD5'); + + if ($contentMD5) { + // We need to validate the integrity of the request + $body = $this->httpRequest->getBody(true); + $this->httpRequest->setBody($body,true); + + if ($contentMD5!=base64_encode(md5($body,true))) { + // content-md5 header did not match md5 signature of body + $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; + } + + } + + if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) + $requestDate = $this->httpRequest->getHeader('Date'); + + if (!$this->validateRFC2616Date($requestDate)) + return false; + + $amzHeaders = $this->getAmzHeaders(); + + $signature = base64_encode( + $this->hmacsha1($secretKey, + $this->httpRequest->getMethod() . "\n" . + $contentMD5 . "\n" . + $this->httpRequest->getHeader('Content-type') . "\n" . + $requestDate . "\n" . + $amzHeaders . + $this->httpRequest->getURI() + ) + ); + + if ($this->signature != $signature) { + + $this->errorCode = self::ERR_INVALIDSIGNATURE; + return false; + + } + + return true; + + } + + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','AWS'); + $this->httpResponse->sendStatus(401); + + } + + /** + * Makes sure the supplied value is a valid RFC2616 date. + * + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * user just supplied the word 'now' for the date header. + * + * This function also makes sure the Date header is within 15 minutes of the operating + * system date, to prevent replay attacks. + * + * @param string $dateHeader + * @return bool + */ + protected function validateRFC2616Date($dateHeader) { + + $date = Sabre_HTTP_Util::parseHTTPDate($dateHeader); + + // Unknown format + if (!$date) { + $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; + } + + $min = new DateTime('-15 minutes'); + $max = new DateTime('+15 minutes'); + + // We allow 15 minutes around the current date/time + if ($date > $max || $date < $min) { + $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; + } + + return $date; + + } + + /** + * Returns a list of AMZ headers + * + * @return void + */ + protected function getAmzHeaders() { + + $amzHeaders = array(); + $headers = $this->httpRequest->getHeaders(); + foreach($headers as $headerName => $headerValue) { + if (strpos(strtolower($headerName),'x-amz-')===0) { + $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n"; + } + } + ksort($amzHeaders); + + $headerStr = ''; + foreach($amzHeaders as $h=>$v) { + $headerStr.=$h.':'.$v; + } + + return $headerStr; + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + $blocksize=64; + if (strlen($key)>$blocksize) + $key=pack('H*', sha1($key)); + $key=str_pad($key,$blocksize,chr(0x00)); + $ipad=str_repeat(chr(0x36),$blocksize); + $opad=str_repeat(chr(0x5c),$blocksize); + $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message)))); + return $hmac; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php new file mode 100755 index 00000000..0abc4bcb --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php @@ -0,0 +1,111 @@ +httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + /** + * Sets an alternative HTTP response object + * + * @param Sabre_HTTP_Response $response + * @return void + */ + public function setHTTPResponse(Sabre_HTTP_Response $response) { + + $this->httpResponse = $response; + + } + + /** + * Sets an alternative HTTP request object + * + * @param Sabre_HTTP_Request $request + * @return void + */ + public function setHTTPRequest(Sabre_HTTP_Request $request) { + + $this->httpRequest = $request; + + } + + + /** + * Sets the realm + * + * The realm is often displayed in authentication dialog boxes + * Commonly an application name displayed here + * + * @param string $realm + * @return void + */ + public function setRealm($realm) { + + $this->realm = $realm; + + } + + /** + * Returns the realm + * + * @return string + */ + public function getRealm() { + + return $this->realm; + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + abstract public function requireLogin(); + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php new file mode 100755 index 00000000..c0231b4e --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php @@ -0,0 +1,61 @@ +httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { + + return array($user,$pass); + + } + + // Most other webservers + $auth = $this->httpRequest->getHeader('Authorization'); + + if (!$auth) return false; + + if (strpos(strtolower($auth),'basic')!==0) return false; + + return explode(':', base64_decode(substr($auth, 6))); + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); + $this->httpResponse->sendStatus(401); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php new file mode 100755 index 00000000..bce59594 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php @@ -0,0 +1,234 @@ +nonce = uniqid(); + $this->opaque = md5($this->realm); + parent::__construct(); + + } + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return void + */ + public function init() { + + $digest = $this->getDigest(); + $this->digestParts = $this->parseDigest($digest); + + } + + /** + * Sets the quality of protection value. + * + * Possible values are: + * Sabre_HTTP_DigestAuth::QOP_AUTH + * Sabre_HTTP_DigestAuth::QOP_AUTHINT + * + * Multiple values can be specified using logical OR. + * + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * request body to be md5'ed, which can put strains on CPU and memory. + * + * @param int $qop + * @return void + */ + public function setQOP($qop) { + + $this->qop = $qop; + + } + + /** + * Validates the user. + * + * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); + * + * @param string $A1 + * @return bool + */ + public function validateA1($A1) { + + $this->A1 = $A1; + return $this->validate(); + + } + + /** + * Validates authentication through a password. The actual password must be provided here. + * It is strongly recommended not store the password in plain-text and use validateA1 instead. + * + * @param string $password + * @return bool + */ + public function validatePassword($password) { + + $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); + return $this->validate(); + + } + + /** + * Returns the username for the request + * + * @return string + */ + public function getUsername() { + + return $this->digestParts['username']; + + } + + /** + * Validates the digest challenge + * + * @return bool + */ + protected function validate() { + + $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri']; + + if ($this->digestParts['qop']=='auth-int') { + // Making sure we support this qop value + if (!($this->qop & self::QOP_AUTHINT)) return false; + // We need to add an md5 of the entire request body to the A2 part of the hash + $body = $this->httpRequest->getBody(true); + $this->httpRequest->setBody($body,true); + $A2 .= ':' . md5($body); + } else { + + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; + } + + $A2 = md5($A2); + + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + + return $this->digestParts['response']==$validResponse; + + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $qop = ''; + switch($this->qop) { + case self::QOP_AUTH : $qop = 'auth'; break; + case self::QOP_AUTHINT : $qop = 'auth-int'; break; + case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break; + } + + $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->httpResponse->sendStatus(401); + + } + + + /** + * This method returns the full digest string. + * + * It should be compatibile with mod_php format and other webservers. + * + * If the header could not be found, null will be returned + * + * @return mixed + */ + public function getDigest() { + + // mod_php + $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST'); + if ($digest) return $digest; + + // most other servers + $digest = $this->httpRequest->getHeader('Authorization'); + + if ($digest && strpos(strtolower($digest),'digest')===0) { + return substr($digest,7); + } else { + return null; + } + + } + + + /** + * Parses the different pieces of the digest string into an array. + * + * This method returns false if an incomplete digest was supplied + * + * @param string $digest + * @return mixed + */ + protected function parseDigest($digest) { + + // protect against missing data + $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); + $data = array(); + + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php new file mode 100755 index 00000000..8a0059bc --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php @@ -0,0 +1,220 @@ +_SERVER = $serverData; + else $this->_SERVER =& $_SERVER; + + } + + /** + * Returns the value for a specific http header. + * + * This method returns null if the header did not exist. + * + * @param string $name + * @return string + */ + public function getHeader($name) { + + $serverName = 'HTTP_' . strtoupper(str_replace(array('-'),array('_'),$name)); + return isset($this->_SERVER[$serverName])?$this->_SERVER[$serverName]:null; + + } + + /** + * Returns all (known) HTTP headers. + * + * All headers are converted to lower-case, and additionally all underscores + * are automatically converted to dashes + * + * @return array + */ + public function getHeaders() { + + $hdrs = array(); + foreach($this->_SERVER as $key=>$value) { + + if (strpos($key,'HTTP_')===0) { + $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value; + } + + } + + return $hdrs; + + } + + /** + * Returns the HTTP request method + * + * This is for example POST or GET + * + * @return string + */ + public function getMethod() { + + return $this->_SERVER['REQUEST_METHOD']; + + } + + /** + * Returns the requested uri + * + * @return string + */ + public function getUri() { + + return $this->_SERVER['REQUEST_URI']; + + } + + /** + * Will return protocol + the hostname + the uri + * + * @return void + */ + public function getAbsoluteUri() { + + // Checking if the request was made through HTTPS. The last in line is for IIS + $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off'); + return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri(); + + } + + /** + * Returns everything after the ? from the current url + * + * @return string + */ + public function getQueryString() { + + return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:''; + + } + + /** + * Returns the HTTP request body body + * + * This method returns a readable stream resource. + * If the asString parameter is set to true, a string is sent instead. + * + * @param bool asString + * @return resource + */ + public function getBody($asString = false) { + + if (is_null($this->body)) { + if (!is_null(self::$defaultInputStream)) { + $this->body = self::$defaultInputStream; + } else { + $this->body = fopen('php://input','r'); + self::$defaultInputStream = $this->body; + } + } + if ($asString) { + $body = stream_get_contents($this->body); + return $body; + } else { + return $this->body; + } + + } + + /** + * Sets the contents of the HTTP requet body + * + * This method can either accept a string, or a readable stream resource. + * + * If the setAsDefaultInputStream is set to true, it means for this run of the + * script the supplied body will be used instead of php://input. + * + * @param mixed $body + * @param bool $setAsDefaultInputStream + * @return void + */ + public function setBody($body,$setAsDefaultInputStream = false) { + + if(is_resource($body)) { + $this->body = $body; + } else { + + $stream = fopen('php://temp','r+'); + fputs($stream,$body); + rewind($stream); + // String is assumed + $this->body = $stream; + } + if ($setAsDefaultInputStream) { + self::$defaultInputStream = $this->body; + } + + } + + /** + * Returns a specific item from the _SERVER array. + * + * Do not rely on this feature, it is for internal use only. + * + * @param string $field + * @return string + */ + public function getRawServerValue($field) { + + return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php new file mode 100755 index 00000000..15a26874 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php @@ -0,0 +1,151 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'Ok', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authorative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC 4918 + 208 => 'Already Reported', // RFC 5842 + 226 => 'IM Used', // RFC 3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 400 => 'Bad request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC 2324 + 422 => 'Unprocessable Entity', // RFC 4918 + 423 => 'Locked', // RFC 4918 + 424 => 'Failed Dependency', // RFC 4918 + 426 => 'Upgrade required', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Unsufficient Storage', // RFC 4918 + 508 => 'Loop Detected', // RFC 5842 + 510 => 'Not extended', + ); + + return 'HTTP/1.1 ' . $code . ' ' . $msg[$code]; + + } + + /** + * Sends an HTTP status header to the client + * + * @param int $code HTTP status code + * @return void + */ + public function sendStatus($code) { + + if (!headers_sent()) + return header($this->getStatusMessage($code)); + else return false; + + } + + /** + * Sets an HTTP header for the response + * + * @param string $name + * @param string $value + * @return void + */ + public function setHeader($name, $value, $replace = true) { + + $value = str_replace(array("\r","\n"),array('\r','\n'),$value); + if (!headers_sent()) + return header($name . ': ' . $value, $replace); + else return false; + + } + + /** + * Sets a bunch of HTTP Headers + * + * headersnames are specified as keys, value in the array value + * + * @param array $headers + * @return void + */ + public function setHeaders(array $headers) { + + foreach($headers as $key=>$value) + $this->setHeader($key, $value); + + } + + /** + * Sends the entire response body + * + * This method can accept either an open filestream, or a string. + * + * @param mixed $body + * @return void + */ + public function sendBody($body) { + + if (is_resource($body)) { + + fpassthru($body); + + } else { + + // We assume a string + echo $body; + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php new file mode 100755 index 00000000..02ae1c0f --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php @@ -0,0 +1,65 @@ += 0) + return new DateTime('@' . $realDate, new DateTimeZone('UTC')); + + return false; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php new file mode 100755 index 00000000..5372a49c --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php @@ -0,0 +1,24 @@ + + +
                        +: +
                        + From aad9e177ddc23e5e6b5194dc3a47bf9b0867bacf Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sun, 12 Dec 2010 13:30:34 +0000 Subject: [PATCH 116/300] fixed hard coded bucket name in aws_s3 helper --- 3.0/modules/aws_s3/helpers/aws_s3.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/modules/aws_s3/helpers/aws_s3.php b/3.0/modules/aws_s3/helpers/aws_s3.php index 9b5b5eb5..891ba0d3 100644 --- a/3.0/modules/aws_s3/helpers/aws_s3.php +++ b/3.0/modules/aws_s3/helpers/aws_s3.php @@ -53,7 +53,7 @@ class aws_s3_Core { "&Signature=" . urlencode(self::getHash("GET\n\n\n" . (time() + module::get_var("aws_s3", "sig_exp")) . "\n/" . $host . "/" . $resource)); self::get_s3(); - S3::getAuthenticatedURL("danneh-org", $resource, module::get_var("aws_s3", "sig_exp")); + S3::getAuthenticatedURL(module::get_var("aws_s3", "bucket_name"), $resource, module::get_var("aws_s3", "sig_exp")); } else $url .= "?m=" . ($updated ? $updated : time()); From 6ae1741f053658d723fce965958feea3abde786f Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Tue, 14 Dec 2010 16:17:46 -0800 Subject: [PATCH 117/300] Don't mark add_user_to_group() and remove_user_from_group() static since they not static in IdentityProvider_Driver. --- 3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php | 4 ++-- 3.1/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php b/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php index 9045b4dc..d4e096ff 100644 --- a/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php +++ b/3.0/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) { + public 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) { + public function remove_user_from_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } } // End Identity Gallery Driver diff --git a/3.1/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php b/3.1/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php index 9045b4dc..d4e096ff 100644 --- a/3.1/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php +++ b/3.1/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) { + public 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) { + public function remove_user_from_group($user, $group) { throw new Exception("@todo INVALID OPERATION"); } } // End Identity Gallery Driver From 4c383fb0b28aa07b85caf60a6e3a4bae119a7a9b Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Wed, 15 Dec 2010 02:09:47 +0100 Subject: [PATCH 118/300] Reworked the entire WebDAV glue in order to get the move functionality to work properly. Cleanups. --- 3.0/modules/webdav/controllers/webdav.php | 209 ++++++++++++++++------ 1 file changed, 158 insertions(+), 51 deletions(-) diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php index 6d2c443a..b030c425 100644 --- a/3.0/modules/webdav/controllers/webdav.php +++ b/3.0/modules/webdav/controllers/webdav.php @@ -6,15 +6,19 @@ include 'Sabre/autoload.php'; class webdav_Controller extends Controller { public function gallery() { - $tree = new Gallery3Album(''); + $root = new Gallery3Album(''); + $tree = new Gallery3DAVTree($root); - $lock_backend = new Sabre_DAV_Locks_Backend_FS(TMPPATH . 'sabredav'); - $lock = new Sabre_DAV_Locks_Plugin($lock_backend); + // Skip the lock plugin for now, we don't want Finder to get write support for the time being. + //$lock_backend = new Sabre_DAV_Locks_Backend_FS(TMPPATH . 'sabredav'); + //$lock = new Sabre_DAV_Locks_Plugin($lock_backend); $filter = new Sabre_DAV_TemporaryFileFilterPlugin(TMPPATH . 'sabredav'); $server = new Sabre_DAV_Server($tree); + #$server = new Gallery3DAV($tree); + $server->setBaseUri(url::site('webdav/gallery')); - $server->addPlugin($lock); + //$server->addPlugin($lock); $server->addPlugin($filter); $this->doAuthenticate(); @@ -43,50 +47,161 @@ class webdav_Controller extends Controller { } } +class Gallery3DAVCache { + protected static $cache; + private static $instance; + + private function __construct() { + $this->cache = array(); + } + + private function encodePath($path) + { + $path = trim($path, '/'); + $encodedArray = array(); + foreach (split('/', $path) as $part) + { + $encodedArray[] = rawurlencode($part); + } + + $path = join('/', $encodedArray); + + return $path; + } + + public function getAlbumOf($path) { + $path = substr($path, 0, strrpos($path, '/')); + + return $this->getItemAt($path); + } + + public function getItemAt($path) + { + $path = trim($path, '/'); + $path = $this->encodePath($path); + + if (isset($this->cache[$path])) { + return $this->cache[$path]; + } + + $item = ORM::factory("item") + ->where("relative_path_cache", "=", $path) + ->find(); + + $this->cache[$path] = $item; + return $item; + } + + public static function singleton() { + if (!isset(self::$instance)) { + $c = __CLASS__; + self::$instance = new $c; + } + + return self::$instance; + } + + public function __clone() {} + +} + +class Gallery3DAVTree extends Sabre_DAV_Tree { + protected $rootNode; + + public function __construct(Sabre_DAV_ICollection $rootNode) { + $this->cache = Gallery3DAVCache::singleton(); + $this->rootNode = $rootNode; + } + + + public function move($source, $target) { + $sourceItem = $this->cache->getItemAt($source); + $targetItem = $this->cache->getAlbumOf($target); + + if (! access::can('view', $sourceItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('edit', $sourceItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('view', $targetItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('edit', $targetItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + + $sourceItem->parent_id = $targetItem->id; + $sourceItem->save(); + return true; + } + + public function getNodeForPath($path) { + + $path = trim($path,'/'); + + $currentNode = $this->rootNode; + $item = $this->cache->getItemAt($path); + + if (! $item->id) { + throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path); + } + + if ($item->type == 'album') { $currentNode = new Gallery3Album($path); } + else { $currentNode = new Gallery3File($path); } + + return $currentNode; + } +} class Gallery3Album extends Sabre_DAV_Directory { private $item; private $stat; + private $path; - function __construct($name) { - $this->item = ORM::factory("item") - ->where("relative_path_cache", "=", rawurlencode($name)) - ->find(); + function __construct($path) { + $this->cache = Gallery3DAVCache::singleton(); + $this->path = $path; + $this->item = $this->cache->getItemAt($path); } function getName() { return $this->item->name; } - function getChild($name) { - $rp = $this->item->relative_path() . '/' . rawurlencode($name); - if (substr($rp,0,1) == '/') { $rp = substr($rp, 1); } + function getChildren() { + $return = array(); + foreach ($this->item->children() as $child) { + $item = $this->getChild($child->name); + if ($item != false) { + $return[] = $item; + } + } + return $return; + } + + function getChild($name) { + $rp = $this->path . '/' . $name; - $child = ORM::factory("item") - ->where("relative_path_cache", "=", $rp) - ->find(); + $child = $this->cache->getItemAt($rp); + + if (! $child->id) { + throw new Sabre_DAV_Exception_FileNotFound('Access denied'); + } if (! access::can('view', $child)) { - return false; + return false; }; if ($child->type == 'album') { - return new Gallery3Album($this->item->relative_path() . $child->name); + return new Gallery3Album($rp); } else { return new Gallery3File($rp); } } - + public function createFile($name, $data = null) { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; - if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; if (substr($name, 0, 1) == '.') { return true; }; $tempfile = tempnam(TMPPATH, 'dav'); $target = fopen($tempfile, 'wb'); stream_copy_to_stream($data, $target); fclose($target); - + $parent_id = $this->item->__get('id'); $item = ORM::factory("item"); $item->name = $name; @@ -99,8 +214,8 @@ class Gallery3Album extends Sabre_DAV_Directory { } public function createDirectory($name) { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; - if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $parent_id = $this->item->__get('id'); $album = ORM::factory("item"); @@ -114,28 +229,21 @@ class Gallery3Album extends Sabre_DAV_Directory { $this->item = ORM::factory("item")->where('id', '=', $parent_id); } + function getLastModified() { + return $this->item->updated; + } + function setName($name) { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->name = $name; $this->item->save(); } public function delete() { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->delete(); } - - function getChildren() { - $return = array(); - foreach ($this->item->children() as $child) { - $item = $this->getChild($child->name); - if ($item != false) { - $return[] = $item; - } - } - return $return; - } } class Gallery3File extends Sabre_DAV_File { @@ -144,36 +252,35 @@ class Gallery3File extends Sabre_DAV_File { private $path; function __construct($path) { - $this->item = ORM::factory("item") - ->where("relative_path_cache", "=", $path) - ->find(); - - if (access::can('view_full', $this->item)) { - $this->stat = stat($this->item->file_path()); - $this->path = $this->item->file_path(); - } else { - $this->stat = stat($this->item->resize_path()); - $this->path = $this->item->resize_path(); - } + $this->cache = Gallery3DAVCache::singleton(); + $this->item = $this->cache->getItemAt($path); + + if (access::can('view_full', $this->item)) { + $this->stat = stat($this->item->file_path()); + $this->path = $this->item->file_path(); + } else { + $this->stat = stat($this->item->resize_path()); + $this->path = $this->item->resize_path(); + } } public function delete() { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->delete(); } function setName($name) { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->name = $name; $this->item->save(); } public function getLastModified() { - return $this->stat[9]; + return $this->item->updated; } function get() { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; return fopen($this->path,'r'); } @@ -186,7 +293,7 @@ class Gallery3File extends Sabre_DAV_File { } function getETag() { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; return '"' . md5($this->item->file_path()) . '"'; } } From 879fc2fbf174d973d0e22751ae34d0553090a06a Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sat, 27 Nov 2010 06:30:44 +0800 Subject: [PATCH 119/300] removed debug file - security reasons --- 3.0/modules/transcode/debug.php | 50 --------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 3.0/modules/transcode/debug.php diff --git a/3.0/modules/transcode/debug.php b/3.0/modules/transcode/debug.php deleted file mode 100644 index 7e9d31a9..00000000 --- a/3.0/modules/transcode/debug.php +++ /dev/null @@ -1,50 +0,0 @@ -ffmpeg info"; - $ffmpegPath = whereis("ffmpeg"); - echo "path: " . $ffmpegPath . "
                        "; - $version = @shell_exec($ffmpegPath . " -version"); $version = explode("\n", $version); $version = $version[0]; $version = explode(" ", $version); $version = $version[1]; - echo "version: " . $version . "
                        "; - echo "codecs:
                        " . @shell_exec($ffmpegPath . " -codecs 2>&1") . "

                        "; - echo "formats:
                        " . @shell_exec($ffmpegPath . " -formats") . "

                        "; -} - -function whereis($app) { - $op = @shell_exec("whereis " . $app); - if ($op != "") { - $op = explode(" ", $op); - for ($i = 1; $i < count($op); $i++) { - if (file_exists($op[$i]) && !is_dir($op[$i])) - return $op[$i]; - } - } - return false; -} - -/* -stdClass Object -( - [video] => stdClass Object - ( - [codec] => h264, - [height] => 576 - [width] => 640 - ) - - [audio] => stdClass Object - ( - ) - -) - - */ \ No newline at end of file From 37335dec314a3deb1c5a0f022947dabdb3e29566 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sun, 28 Nov 2010 10:35:11 +0800 Subject: [PATCH 120/300] Oops, add in security. Only show viewable children. --- 3.1/modules/albumtree/views/albumtree_block.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/albumtree/views/albumtree_block.html.php b/3.1/modules/albumtree/views/albumtree_block.html.php index 7cf66799..4a73c333 100644 --- a/3.1/modules/albumtree/views/albumtree_block.html.php +++ b/3.1/modules/albumtree/views/albumtree_block.html.php @@ -10,7 +10,7 @@ -children(null, null, array(array("type", "=", "album"))) as $child): ?> +viewable()->children(null, null, array(array("type", "=", "album"))) as $child): ?> From 26dd24e702aa9747067a14ba84efdd709e0d9431 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sat, 27 Nov 2010 22:30:48 +0800 Subject: [PATCH 121/300] Just a test for cancel button. Need to be polished. --- 3.0/themes/sobriety/css/screen.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/3.0/themes/sobriety/css/screen.css b/3.0/themes/sobriety/css/screen.css index 7418c954..bf527138 100644 --- a/3.0/themes/sobriety/css/screen.css +++ b/3.0/themes/sobriety/css/screen.css @@ -801,6 +801,18 @@ div#g-action-status { height: 8em; } +#g-dialog input[type=submit].submit { + float: right; +} + +#g-dialog a.g-cancel { + float: right; + -moz-appearance: button; + -webkit-appearance: push-button; + /*clear: left;*/ +} + + #g-add-photos-canvas-sd { height: 33px; /*margin-right: 63px;*/ From e7316975d2f2f30e5d14f4f732244e90cdf783f8 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 07:25:26 +0800 Subject: [PATCH 122/300] Initial commit. There is probably many bugs, but it seems to work... --- 3.0/modules/user_chroot/helpers/MY_access.php | 61 ++++++++++ 3.0/modules/user_chroot/helpers/MY_item.php | 34 ++++++ 3.0/modules/user_chroot/helpers/MY_url.php | 47 ++++++++ .../user_chroot/helpers/user_chroot.php | 41 +++++++ .../user_chroot/helpers/user_chroot_event.php | 112 ++++++++++++++++++ .../helpers/user_chroot_installer.php | 40 +++++++ .../user_chroot/libraries/MY_ORM_MPTT.php | 61 ++++++++++ .../user_chroot/models/user_chroot.php | 22 ++++ 3.0/modules/user_chroot/module.info | 3 + 9 files changed, 421 insertions(+) create mode 100644 3.0/modules/user_chroot/helpers/MY_access.php create mode 100644 3.0/modules/user_chroot/helpers/MY_item.php create mode 100644 3.0/modules/user_chroot/helpers/MY_url.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot_event.php create mode 100644 3.0/modules/user_chroot/helpers/user_chroot_installer.php create mode 100644 3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php create mode 100644 3.0/modules/user_chroot/models/user_chroot.php create mode 100644 3.0/modules/user_chroot/module.info diff --git a/3.0/modules/user_chroot/helpers/MY_access.php b/3.0/modules/user_chroot/helpers/MY_access.php new file mode 100644 index 00000000..5f73b4ca --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_access.php @@ -0,0 +1,61 @@ +id == identity::active_user()->id && user_chroot::album() ) { + if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { + return false; + } + } + + return parent::user_can($user, $perm_name, $item); + } +} diff --git a/3.0/modules/user_chroot/helpers/MY_item.php b/3.0/modules/user_chroot/helpers/MY_item.php new file mode 100644 index 00000000..423b8ddc --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_item.php @@ -0,0 +1,34 @@ +and_open() + ->and_where("items.left_ptr", ">=", user_chroot::album()->left_ptr) + ->and_where("items.right_ptr", "<=", user_chroot::album()->right_ptr) + ->close(); + } + + return $model; + } +} diff --git a/3.0/modules/user_chroot/helpers/MY_url.php b/3.0/modules/user_chroot/helpers/MY_url.php new file mode 100644 index 00000000..31818fa4 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/MY_url.php @@ -0,0 +1,47 @@ +relative_url().'/'.Router::$current_uri, '/'); + } + + return parent::parse_url(); + } + + static function site($uri = '', $protocol = FALSE) { + if( user_chroot::album() ) { + $uri = preg_replace('#^'.user_chroot::album()->relative_url().'#', '', $uri); + } + + return parent::site($uri, $protocol); + } +} \ No newline at end of file diff --git a/3.0/modules/user_chroot/helpers/user_chroot.php b/3.0/modules/user_chroot/helpers/user_chroot.php new file mode 100644 index 00000000..4d0a8499 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot.php @@ -0,0 +1,41 @@ +id); + if( $user_chroot->loaded() && $user_chroot->album_id != 0 ) { + $item = ORM::factory("item", $user_chroot->album_id); + if( $item->loaded() ) { + self::$_album = $item; + } + } + } + + return self::$_album; + } +} \ No newline at end of file diff --git a/3.0/modules/user_chroot/helpers/user_chroot_event.php b/3.0/modules/user_chroot/helpers/user_chroot_event.php new file mode 100644 index 00000000..c43c6dc4 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot_event.php @@ -0,0 +1,112 @@ +delete($user->id); + } + + /** + * Called when admin is adding a user + */ + static function user_add_form_admin($user, $form) { + $form->add_user->dropdown("user_chroot") + ->label(t("Root Album")) + ->options(self::createGalleryArray()) + ->selected(0); + } + + /** + * Called after a user has been added + */ + static function user_add_form_admin_completed($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->add_user->user_chroot->value; + $user_chroot->save(); + } + + /** + * Called when admin is editing a user + */ + static function user_edit_form_admin($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + if ($user_chroot->loaded()) { + $selected = $user_chroot->album_id; + } else { + $selected = 0; + } + $form->edit_user->dropdown("user_chroot") + ->label(t("Root Album")) + ->options(self::createGalleryArray()) + ->selected($selected); + } + + /** + * Called after a user had been edited by the admin + */ + static function user_edit_form_admin_completed($user, $form) { + $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); + if ($user_chroot->loaded()) { + $user_chroot->album_id = $form->edit_user->user_chroot->value; + } else { + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->edit_user->user_chroot->value; + } + $user_chroot->save(); + } + + + /** + * Creates an array of galleries + */ + static function createGalleryArray() { + $array[0] = "none"; + $root = ORM::factory("item", 1); + self::tree($root, "", $array); + return $array; + } + + /** + * recursive function to build array for drop down list + */ + static function tree($parent, $dashes, &$array) { + if ($parent->id == "1") { + $array[$parent->id] = ORM::factory("item", 1)->title; + } else { + $array[$parent->id] = "$dashes $parent->name"; + } + + $albums = ORM::factory("item") + ->where("parent_id", "=", $parent->id) + ->where("type", "=", "album") + ->order_by("title", "ASC") + ->find_all(); + foreach ($albums as $album) { + self::tree($album, "-$dashes", $array); + } + return; + } +} diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php new file mode 100644 index 00000000..ee89fb13 --- /dev/null +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -0,0 +1,40 @@ +query("CREATE TABLE IF NOT EXISTS {user_chroots} ( + `id` int(9) NOT NULL, + `album_id` int(9) default NULL, + PRIMARY KEY (`id`), + UNIQUE KEY(`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("user_chroot", 1); + } + + /** + * Drops the table of user chroot when the module is uninstalled. + */ + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {user_chroots};"); + } +} diff --git a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php new file mode 100644 index 00000000..e31124ca --- /dev/null +++ b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php @@ -0,0 +1,61 @@ +model_name = inflector::singular($this->table_name); + } + + /** + * Return the parent of this node + * + * @return ORM + */ + /*function parent() { + if( user_chroot::album() && user_chroot::album()->id == $this->id ) { + return null; + } else { + return parent::parent(); + } + }*/ + + /** + * Return all the parents of this node, in order from root to this node's immediate parent. + * + * @return array ORM + */ + function parents() { + $select = $this + ->where("left_ptr", "<=", $this->left_ptr) + ->where("right_ptr", ">=", $this->right_ptr) + ->where("id", "<>", $this->id) + ->order_by("left_ptr", "ASC"); + + if( user_chroot::album() ) { + $select->where("left_ptr", ">=", user_chroot::album()->left_ptr); + $select->where("right_ptr", "<=", user_chroot::album()->right_ptr); + } + + return $select->find_all(); + } +} diff --git a/3.0/modules/user_chroot/models/user_chroot.php b/3.0/modules/user_chroot/models/user_chroot.php new file mode 100644 index 00000000..30184597 --- /dev/null +++ b/3.0/modules/user_chroot/models/user_chroot.php @@ -0,0 +1,22 @@ + Date: Sat, 27 Nov 2010 06:44:32 +0800 Subject: [PATCH 123/300] added amazon s3 module --- .../aws_s3/controllers/admin_aws_s3.php | 124 ++ .../aws_s3/helpers/MY_embedlinks_block.php | 10 + .../aws_s3/helpers/MY_embedlinks_theme.php | 13 + 3.0/modules/aws_s3/helpers/MY_item.php | 23 + 3.0/modules/aws_s3/helpers/aws_s3.php | 173 +++ 3.0/modules/aws_s3/helpers/aws_s3_event.php | 34 + .../aws_s3/helpers/aws_s3_installer.php | 40 + 3.0/modules/aws_s3/helpers/aws_s3_task.php | 78 + 3.0/modules/aws_s3/lib/s3.php | 1365 +++++++++++++++++ 3.0/modules/aws_s3/models/MY_Item_Model.php | 40 + 3.0/modules/aws_s3/module.info | 3 + .../aws_s3/views/admin_aws_s3.html.php | 13 + 12 files changed, 1916 insertions(+) create mode 100644 3.0/modules/aws_s3/controllers/admin_aws_s3.php create mode 100644 3.0/modules/aws_s3/helpers/MY_embedlinks_block.php create mode 100644 3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php create mode 100644 3.0/modules/aws_s3/helpers/MY_item.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_event.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_installer.php create mode 100644 3.0/modules/aws_s3/helpers/aws_s3_task.php create mode 100644 3.0/modules/aws_s3/lib/s3.php create mode 100644 3.0/modules/aws_s3/models/MY_Item_Model.php create mode 100644 3.0/modules/aws_s3/module.info create mode 100644 3.0/modules/aws_s3/views/admin_aws_s3.html.php diff --git a/3.0/modules/aws_s3/controllers/admin_aws_s3.php b/3.0/modules/aws_s3/controllers/admin_aws_s3.php new file mode 100644 index 00000000..8ce2e7ea --- /dev/null +++ b/3.0/modules/aws_s3/controllers/admin_aws_s3.php @@ -0,0 +1,124 @@ +_get_s3_form(); + + if (request::method() == "post") { + access::verify_csrf(); + + if ($form->validate()) { + module::set_var("aws_s3", "enabled", (isset($_POST['enabled']) ? true : false)); + module::set_var("aws_s3", "access_key", $_POST['access_key']); + module::set_var("aws_s3", "secret_key", $_POST['secret_key']); + module::set_var("aws_s3", "bucket_name", $_POST['bucket_name']); + module::set_var("aws_s3", "g3id", $_POST['g3id']); + + module::set_var("aws_s3", "url_str", $_POST['url_str']); + module::set_var("aws_s3", "sig_exp", $_POST['sig_exp']); + + module::set_var("aws_s3", "use_ssl", (isset($_POST['use_ssl']) ? true : false)); + + if (module::get_var("aws_s3", "enabled") && !module::get_var("aws_s3", "synced", false)) + site_status::warning( + t('Your site has not yet been syncronised with your Amazon S3 bucket. Content will not appear correctly until you perform syncronisation. Fix this now', + array("url" => html::mark_clean(url::site("admin/maintenance/start/aws_s3_task::sync?csrf=__CSRF__"))) + ), "aws_s3_not_synced"); + + + message::success(t("Settings have been saved")); + url::redirect("admin/aws_s3"); + } + else { + message::error(t("There was a problem with the submitted form. Please check your values and try again.")); + } + } + + $v = new Admin_View("admin.html"); + $v->page_title = t("Amazon S3 Configuration"); + $v->content = new View("admin_aws_s3.html"); + $v->content->form = $form; + $v->content->end = ""; + + echo $v; + } + + private function _get_s3_form() { + $form = new Forge("admin/aws_s3", "", "post", array("id" => "g-admin-s3-form")); + + $group = $form->group("aws_s3")->label(t("Amazon S3 Settings")); + + $group ->checkbox("enabled") + ->id("s3-enabled") + ->checked(module::get_var("aws_s3", "enabled")) + ->label("S3 enabled"); + + $group ->input("access_key") + ->id("s3-access-key") + ->label("Access Key ID") + ->value(module::get_var("aws_s3", "access_key")) + ->rules("required") + ->error_messages("required", "This field is required") + ->message('Sign up to Amazon S3'); + + $group ->input("secret_key") + ->id("s3-secret-key") + ->label("Secret Access Key") + ->value(module::get_var("aws_s3", "secret_key")) + ->rules("required") + ->error_messages("required", "This field is required"); + + $group ->input("bucket_name") + ->id("s3-bucket") + ->label("Bucket Name") + ->value(module::get_var("aws_s3", "bucket_name")) + ->rules("required") + ->error_messages("required", "This field is required") + ->message("Note: This module will not create a bucket if it does not already exist. Please ensure you have already created the bucket and the bucket has the correct ACL permissions before continuing."); + + $group ->input("g3id") + ->id("s3-g3id") + ->label("G3 ID") + ->value(module::get_var("aws_s3", "g3id", md5(time()))) + ->rules("required") + ->error_messages("required", "This field is required") + ->message("This field allows for multiple G3 instances running off of a single S3 bucket."); + + $group ->checkbox("use_ssl") + ->id("s3-use-ssl") + ->checked(module::get_var("aws_s3", "use_ssl")) + ->label("Use SSL for S3 transfers"); + + $group = $form->group("cdn_settings")->label(t("CDN Settings")); + + $group ->input("url_str") + ->id("s3-url-str") + ->label("URL String") + ->value(module::get_var("aws_s3", "url_str", "http://{bucket}.s3.amazonaws.com/g3/{guid}/{resource}")) + ->rules("required") + ->message("Configure the URL to access uploaded resources on the CDN. Use the following variables to define and build up the URL:
                        +• {bucket} - Bucket Name
                        +• {guid} - Unique identifier for this gallery installation
                        +• {resource} - The end path to the resource/object"); + + $group ->input("sig_exp") + ->id("sig_exp") + ->label("Private Content Signature Duration") + ->value(module::get_var("aws_s3", "sig_exp", 60)) + ->rules("required") + ->callback("aws_s3::validate_number") + ->error_messages("not_numeric", "The value provided is not numeric. Please enter a number in this field.") + ->message("Set the time in seconds for the generated signature for access to permission-restricted S3 objects

                        +Note: this module does not yet support the creation of signatures to access private objects on S3 via CloudFront CDN."); + + $form ->submit("save") + ->value("Save Settings"); + + + return $form; + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php new file mode 100644 index 00000000..fe251b1c --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php @@ -0,0 +1,10 @@ +item && $theme->item->view_1 == 1) + parent::get($block_id, $theme); + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php new file mode 100644 index 00000000..5313cf79 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_theme.php @@ -0,0 +1,13 @@ +item; + if ($item->view_1 == 1) + return parent::photo_bottom($theme); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/MY_item.php b/3.0/modules/aws_s3/helpers/MY_item.php new file mode 100644 index 00000000..f26aa431 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/MY_item.php @@ -0,0 +1,23 @@ +parent(); + if ($parent->id > 1) { + aws_s3::upload_album_cover($parent); + } + } + + static function remove_album_cover($album) { + parent::remove_album_cover($album); + + if ($album->id > 1) { + aws_s3::remove_album_cover($album); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3.php b/3.0/modules/aws_s3/helpers/aws_s3.php new file mode 100644 index 00000000..9589f495 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3.php @@ -0,0 +1,173 @@ + 0) + return $matches[1]; + return false; + } + + static function log($item) { + if (is_string($item) || is_numeric($item)) {} + else + $item = print_r($item, true); + + $fh = fopen(VARPATH . "modules/aws_s3/log/aws_s3-" . date("Y-m-d") . ".log", "a"); + fwrite($fh, date("Y-m-d H:i:s") . ": " . $item . "\n"); + fclose($fh); + } + + static function upload_item($item) { + self::get_s3(); + + $success_fs = S3::putObjectFile(VARPATH . "albums/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + $success_th = S3::putObjectFile(VARPATH . "thumbs/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + $success_rs = S3::putObjectFile(VARPATH . "resizes/" . $item->relative_path(), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $item->relative_path()), + ($item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + + $success = $success_fs && $success_th && $success_rs; + aws_s3::log("item upload success: " . $success); + } + + static function move_item($old_item, $new_item) { + self::get_s3(); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $old_item->relative_path())); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $old_item->relative_path())); + + S3::copyObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $old_item->relative_path()), + module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $new_item->relative_path()), + ($new_item->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $old_item->relative_path())); + } + + static function remove_item($item) { + self::get_s3(); + + $success_fs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("fs/" . $item->relative_path())); + $success_th = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("th/" . $item->relative_path())); + $success_rs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + self::get_resource_url("rs/" . $item->relative_path())); + + $success = $success_fs && $success_th && $success_rs; + aws_s3::log("s3 delete success: " . $success); + } + + static function upload_album_cover($album) { + self::get_s3(); + + if (file_exists(VARPATH . "resizes/" . $album->relative_path() . "/.album.jpg")) + $success_rs = S3::putObjectFile(VARPATH . "resizes/" . $album->relative_path() . "/.album.jpg", + module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/rs/" . $album->relative_path() . "/.album.jpg", + ($album->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + else + $success_rs = true; + + if (file_exists(VARPATH . "thumbs/" . $album->relative_path() . "/.album.jpg")) + $success_th = S3::putObjectFile(VARPATH . "thumbs/" . $album->relative_path() . "/.album.jpg", + module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/th/" . $album->relative_path() . "/.album.jpg", + ($album->view_1 ? S3::ACL_PUBLIC_READ : S3::ACL_PRIVATE)); + else + $success_th = true; + + $success = $success_rs && $success_th; + aws_s3::log("album cover upload success: " . $success); + } + + static function remove_album_cover($album) { + self::get_s3(); + + $success_th = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/th/" . $album->relative_path() . "/.album.jpg"); + $success_rs = S3::deleteObject(module::get_var("aws_s3", "bucket_name"), + "g3/" . module::get_var("aws_s3", "g3id") . "/rs/" . $album->relative_path() . "/.album.jpg"); + + $success = $success_rs && $success_th; + aws_s3::log("album cover removal success: " . $success); + } + + static function getAuthenticatedURL($bucket, $uri) { + self::get_s3(); + + return S3::getAuthenticatedURL($bucket, $uri, 60); + } + + static function validate_number($field) { + if (preg_match("/\D/", $field->value)) + $field->add_error("not_numeric", 1); + } + + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3_event.php b/3.0/modules/aws_s3/helpers/aws_s3_event.php new file mode 100644 index 00000000..bb46de80 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3_event.php @@ -0,0 +1,34 @@ +get("settings_menu") + ->append( + Menu::factory("link") + ->id("aws_s3_link") + ->label(t("Amazon S3")) + ->url(url::site("admin/aws_s3")) + ); + } + + static function item_created($item) { + if ($item->is_album()) + return true; + + aws_s3::log("Item created - " . $item->id); + aws_s3::upload_item($item); + } + + static function item_deleted($item) { + aws_s3::log("Item deleted - " . $item->id); + aws_s3::remove_item($item); + } + + static function item_moved($new_item, $old_item) { + aws_s3::log("Item moved - " . $item->id); + aws_s3::move_item($old_item, $new_item); + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/helpers/aws_s3_installer.php b/3.0/modules/aws_s3/helpers/aws_s3_installer.php new file mode 100644 index 00000000..75b401b3 --- /dev/null +++ b/3.0/modules/aws_s3/helpers/aws_s3_installer.php @@ -0,0 +1,40 @@ +callback("aws_s3_task::sync") + ->name(t("Syncronise with Amazon S3")) + ->description(t("Syncronise your Gallery 3 data/images with your Amazon S3 bucket")) + ->severity(log::SUCCESS)); + } + + static function sync($task) { + require_once(MODPATH . "aws_s3/lib/s3.php"); + $s3 = new S3(module::get_var("aws_s3", "access_key"), module::get_var("aws_s3", "secret_key")); + + $mode = $task->get("mode", "init"); + switch ($mode) { + case "init": { + aws_s3::log("re-sync task started.."); + batch::start(); + $items = ORM::factory("item")->find_all(); + aws_s3::log("items to sync: " . count($items)); + $task->set("total_count", count($items)); + $task->set("completed", 0); + $task->set("mode", "empty"); + $task->status = "Emptying contents of bucket"; + } break; + case "empty": { // 0 - 10% + aws_s3::log("emptying bucket contents (any files that may already exist in the bucket/prefix path)"); + $bucket = module::get_var("aws_s3", "bucket_name"); + + $resource = aws_s3::get_resource_url(""); + $stuff = array_reverse(S3::getBucket($bucket, $resource)); + foreach ($stuff as $uri => $item) { + aws_s3::log("removing: " . $uri); + S3::deleteObject($bucket, $uri); + } + $task->percent_complete = 10; + $task->set("mode", "upload"); + $task->state = "Commencing upload..."; + } break; + case "upload": { // 10 - 100% + $completed = $task->get("completed", 0); + $items = ORM::factory("item")->find_all(1, $completed); + foreach ($items as $item) { + if ($item->id > 1) { + aws_s3::log("uploading item " . $item->id . " (" . ($completed + 1) . "/" . $task->get("total_count") . ")"); + if ($item->is_album()) + aws_s3::upload_album_cover($item); + else + aws_s3::upload_item($item); + } + $completed++; + } + $task->set("completed", $completed); + $task->percent_complete = round(90 * ($completed / $task->get("total_count"))) + 10; + $task->status = $completed . " of " . $task->get("total_count"). " uploaded."; + + if ($completed == $task->get("total_count")) { + $task->set("mode", "finish"); + } + } break; + case "finish": { + aws_s3::log("completing upload task.."); + $task->percent_complete = 100; + $task->state = "success"; + $task->done = true; + $task->status = "Sync task completed successfully"; + batch::stop(); + module::set_var("aws_s3", "synced", true); + site_status::clear("aws_s3_not_synced"); + } break; + } + + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/lib/s3.php b/3.0/modules/aws_s3/lib/s3.php new file mode 100644 index 00000000..ae9aeb83 --- /dev/null +++ b/3.0/modules/aws_s3/lib/s3.php @@ -0,0 +1,1365 @@ +getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listBuckets(): [%s] %s", $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + $results = array(); + if (!isset($rest->body->Buckets)) return $results; + + if ($detailed) { + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) + $results['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->ID + ); + $results['buckets'] = array(); + foreach ($rest->body->Buckets->Bucket as $b) + $results['buckets'][] = array( + 'name' => (string)$b->Name, 'time' => strtotime((string)$b->CreationDate) + ); + } else + foreach ($rest->body->Buckets->Bucket as $b) $results[] = (string)$b->Name; + + return $results; + } + + + /* + * Get contents for a bucket + * + * If maxKeys is null this method will loop through truncated result sets + * + * @param string $bucket Bucket name + * @param string $prefix Prefix + * @param string $marker Marker (last file listed) + * @param string $maxKeys Max keys (maximum number of keys to return) + * @param string $delimiter Delimiter + * @param boolean $returnCommonPrefixes Set to true to return CommonPrefixes + * @return array | false + */ + public static function getBucket($bucket, $prefix = null, $marker = null, $maxKeys = null, $delimiter = null, $returnCommonPrefixes = false) { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + if ($marker !== null && $marker !== '') $rest->setParameter('marker', $marker); + if ($maxKeys !== null && $maxKeys !== '') $rest->setParameter('max-keys', $maxKeys); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + $response = $rest->getResponse(); + if ($response->error === false && $response->code !== 200) + $response->error = array('code' => $response->code, 'message' => 'Unexpected HTTP status'); + if ($response->error !== false) { + trigger_error(sprintf("S3::getBucket(): [%s] %s", $response->error['code'], $response->error['message']), E_USER_WARNING); + return false; + } + + $results = array(); + + $nextMarker = null; + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->IsTruncated) && + (string)$response->body->IsTruncated == 'false') return $results; + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + // Loop through truncated results if maxKeys isn't specified + if ($maxKeys == null && $nextMarker !== null && (string)$response->body->IsTruncated == 'true') + do { + $rest = new S3Request('GET', $bucket, ''); + if ($prefix !== null && $prefix !== '') $rest->setParameter('prefix', $prefix); + $rest->setParameter('marker', $nextMarker); + if ($delimiter !== null && $delimiter !== '') $rest->setParameter('delimiter', $delimiter); + + if (($response = $rest->getResponse(true)) == false || $response->code !== 200) break; + + if (isset($response->body, $response->body->Contents)) + foreach ($response->body->Contents as $c) { + $results[(string)$c->Key] = array( + 'name' => (string)$c->Key, + 'time' => strtotime((string)$c->LastModified), + 'size' => (int)$c->Size, + 'hash' => substr((string)$c->ETag, 1, -1) + ); + $nextMarker = (string)$c->Key; + } + + if ($returnCommonPrefixes && isset($response->body, $response->body->CommonPrefixes)) + foreach ($response->body->CommonPrefixes as $c) + $results[(string)$c->Prefix] = array('prefix' => (string)$c->Prefix); + + if (isset($response->body, $response->body->NextMarker)) + $nextMarker = (string)$response->body->NextMarker; + + } while ($response !== false && (string)$response->body->IsTruncated == 'true'); + + return $results; + } + + + /** + * Put a bucket + * + * @param string $bucket Bucket name + * @param constant $acl ACL flag + * @param string $location Set as "EU" to create buckets hosted in Europe + * @return boolean + */ + public static function putBucket($bucket, $acl = self::ACL_PRIVATE, $location = false) { + $rest = new S3Request('PUT', $bucket, ''); + $rest->setAmzHeader('x-amz-acl', $acl); + + if ($location !== false) { + $dom = new DOMDocument; + $createBucketConfiguration = $dom->createElement('CreateBucketConfiguration'); + $locationConstraint = $dom->createElement('LocationConstraint', strtoupper($location)); + $createBucketConfiguration->appendChild($locationConstraint); + $dom->appendChild($createBucketConfiguration); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + } + $rest = $rest->getResponse(); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::putBucket({$bucket}, {$acl}, {$location}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Delete an empty bucket + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function deleteBucket($bucket) { + $rest = new S3Request('DELETE', $bucket); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteBucket({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Create input info array for putObject() + * + * @param string $file Input file + * @param mixed $md5sum Use MD5 hash (supply a string if you want to use your own) + * @return array | false + */ + public static function inputFile($file, $md5sum = true) { + if (!file_exists($file) || !is_file($file) || !is_readable($file)) { + trigger_error('S3::inputFile(): Unable to open input file: '.$file, E_USER_WARNING); + return false; + } + return array('file' => $file, 'size' => filesize($file), + 'md5sum' => $md5sum !== false ? (is_string($md5sum) ? $md5sum : + base64_encode(md5_file($file, true))) : ''); + } + + + /** + * Create input array info for putObject() with a resource + * + * @param string $resource Input resource to read from + * @param integer $bufferSize Input byte size + * @param string $md5sum MD5 hash to send (optional) + * @return array | false + */ + public static function inputResource(&$resource, $bufferSize, $md5sum = '') { + if (!is_resource($resource) || $bufferSize < 0) { + trigger_error('S3::inputResource(): Invalid resource or buffer size', E_USER_WARNING); + return false; + } + $input = array('size' => $bufferSize, 'md5sum' => $md5sum); + $input['fp'] =& $resource; + return $input; + } + + + /** + * Put an object + * + * @param mixed $input Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param array $requestHeaders Array of request headers or content type as a string + * @return boolean + */ + public static function putObject($input, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + if ($input === false) return false; + $rest = new S3Request('PUT', $bucket, $uri); + + if (is_string($input)) $input = array( + 'data' => $input, 'size' => strlen($input), + 'md5sum' => base64_encode(md5($input, true)) + ); + + // Data + if (isset($input['fp'])) + $rest->fp =& $input['fp']; + elseif (isset($input['file'])) + $rest->fp = @fopen($input['file'], 'rb'); + elseif (isset($input['data'])) + $rest->data = $input['data']; + + // Content-Length (required) + if (isset($input['size']) && $input['size'] >= 0) + $rest->size = $input['size']; + else { + if (isset($input['file'])) + $rest->size = filesize($input['file']); + elseif (isset($input['data'])) + $rest->size = strlen($input['data']); + } + + // Custom request headers (Content-Type, Content-Disposition, Content-Encoding) + if (is_array($requestHeaders)) + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + elseif (is_string($requestHeaders)) // Support for legacy contentType parameter + $input['type'] = $requestHeaders; + + // Content-Type + if (!isset($input['type'])) { + if (isset($requestHeaders['Content-Type'])) + $input['type'] =& $requestHeaders['Content-Type']; + elseif (isset($input['file'])) + $input['type'] = self::__getMimeType($input['file']); + else + $input['type'] = 'application/octet-stream'; + } + + // We need to post with Content-Length and Content-Type, MD5 is optional + if ($rest->size >= 0 && ($rest->fp !== false || $rest->data !== false)) { + $rest->setHeader('Content-Type', $input['type']); + if (isset($input['md5sum'])) $rest->setHeader('Content-MD5', $input['md5sum']); + + $rest->setAmzHeader('x-amz-acl', $acl); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->getResponse(); + } else + $rest->response->error = array('code' => 0, 'message' => 'Missing input parameters'); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::putObject(): [%s] %s", $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Put an object from a file (legacy function) + * + * @param string $file Input file path + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectFile($file, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = null) { + return self::putObject(self::inputFile($file), $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Put an object from a string (legacy function) + * + * @param string $string Input data + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Array of x-amz-meta-* headers + * @param string $contentType Content type + * @return boolean + */ + public static function putObjectString($string, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $contentType = 'text/plain') { + return self::putObject($string, $bucket, $uri, $acl, $metaHeaders, $contentType); + } + + + /** + * Get an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param mixed $saveTo Filename or resource to write to + * @return mixed + */ + public static function getObject($bucket, $uri, $saveTo = false) { + $rest = new S3Request('GET', $bucket, $uri); + if ($saveTo !== false) { + if (is_resource($saveTo)) + $rest->fp =& $saveTo; + else + if (($rest->fp = @fopen($saveTo, 'wb')) !== false) + $rest->file = realpath($saveTo); + else + $rest->response->error = array('code' => 0, 'message' => 'Unable to open save file for writing: '.$saveTo); + } + if ($rest->response->error === false) $rest->getResponse(); + + if ($rest->response->error === false && $rest->response->code !== 200) + $rest->response->error = array('code' => $rest->response->code, 'message' => 'Unexpected HTTP status'); + if ($rest->response->error !== false) { + trigger_error(sprintf("S3::getObject({$bucket}, {$uri}): [%s] %s", + $rest->response->error['code'], $rest->response->error['message']), E_USER_WARNING); + return false; + } + return $rest->response; + } + + + /** + * Get object information + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param boolean $returnInfo Return response information + * @return mixed | false + */ + public static function getObjectInfo($bucket, $uri, $returnInfo = true) { + $rest = new S3Request('HEAD', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && ($rest->code !== 200 && $rest->code !== 404)) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getObjectInfo({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return $rest->code == 200 ? $returnInfo ? $rest->headers : true : false; + } + + + /** + * Copy an object + * + * @param string $bucket Source bucket name + * @param string $uri Source object URI + * @param string $bucket Destination bucket name + * @param string $uri Destination object URI + * @param constant $acl ACL constant + * @param array $metaHeaders Optional array of x-amz-meta-* headers + * @param array $requestHeaders Optional array of request headers (content type, disposition, etc.) + * @return mixed | false + */ + public static function copyObject($srcBucket, $srcUri, $bucket, $uri, $acl = self::ACL_PRIVATE, $metaHeaders = array(), $requestHeaders = array()) { + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setHeader('Content-Length', 0); + foreach ($requestHeaders as $h => $v) $rest->setHeader($h, $v); + foreach ($metaHeaders as $h => $v) $rest->setAmzHeader('x-amz-meta-'.$h, $v); + $rest->setAmzHeader('x-amz-acl', $acl); + $rest->setAmzHeader('x-amz-copy-source', sprintf('/%s/%s', $srcBucket, $srcUri)); + if (sizeof($requestHeaders) > 0 || sizeof($metaHeaders) > 0) + $rest->setAmzHeader('x-amz-metadata-directive', 'REPLACE'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::copyObject({$srcBucket}, {$srcUri}, {$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return isset($rest->body->LastModified, $rest->body->ETag) ? array( + 'time' => strtotime((string)$rest->body->LastModified), + 'hash' => substr((string)$rest->body->ETag, 1, -1) + ) : false; + } + + + /** + * Set logging for a bucket + * + * @param string $bucket Bucket name + * @param string $targetBucket Target bucket (where logs are stored) + * @param string $targetPrefix Log prefix (e,g; domain.com-) + * @return boolean + */ + public static function setBucketLogging($bucket, $targetBucket, $targetPrefix = null) { + // The S3 log delivery group has to be added to the target bucket's ACP + if ($targetBucket !== null && ($acp = self::getAccessControlPolicy($targetBucket, '')) !== false) { + // Only add permissions to the target bucket when they do not exist + $aclWriteSet = false; + $aclReadSet = false; + foreach ($acp['acl'] as $acl) + if ($acl['type'] == 'Group' && $acl['uri'] == 'http://acs.amazonaws.com/groups/s3/LogDelivery') { + if ($acl['permission'] == 'WRITE') $aclWriteSet = true; + elseif ($acl['permission'] == 'READ_ACP') $aclReadSet = true; + } + if (!$aclWriteSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'WRITE' + ); + if (!$aclReadSet) $acp['acl'][] = array( + 'type' => 'Group', 'uri' => 'http://acs.amazonaws.com/groups/s3/LogDelivery', 'permission' => 'READ_ACP' + ); + if (!$aclReadSet || !$aclWriteSet) self::setAccessControlPolicy($targetBucket, '', $acp); + } + + $dom = new DOMDocument; + $bucketLoggingStatus = $dom->createElement('BucketLoggingStatus'); + $bucketLoggingStatus->setAttribute('xmlns', 'http://s3.amazonaws.com/doc/2006-03-01/'); + if ($targetBucket !== null) { + if ($targetPrefix == null) $targetPrefix = $bucket . '-'; + $loggingEnabled = $dom->createElement('LoggingEnabled'); + $loggingEnabled->appendChild($dom->createElement('TargetBucket', $targetBucket)); + $loggingEnabled->appendChild($dom->createElement('TargetPrefix', $targetPrefix)); + // TODO: Add TargetGrants? + $bucketLoggingStatus->appendChild($loggingEnabled); + } + $dom->appendChild($bucketLoggingStatus); + + $rest = new S3Request('PUT', $bucket, ''); + $rest->setParameter('logging', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setBucketLogging({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get logging status for a bucket + * + * This will return false if logging is not enabled. + * Note: To enable logging, you also need to grant write access to the log group + * + * @param string $bucket Bucket name + * @return array | false + */ + public static function getBucketLogging($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('logging', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLogging({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + if (!isset($rest->body->LoggingEnabled)) return false; // No logging + return array( + 'targetBucket' => (string)$rest->body->LoggingEnabled->TargetBucket, + 'targetPrefix' => (string)$rest->body->LoggingEnabled->TargetPrefix, + ); + } + + + /** + * Disable bucket logging + * + * @param string $bucket Bucket name + * @return boolean + */ + public static function disableBucketLogging($bucket) { + return self::setBucketLogging($bucket, null); + } + + + /** + * Get a bucket's location + * + * @param string $bucket Bucket name + * @return string | false + */ + public static function getBucketLocation($bucket) { + $rest = new S3Request('GET', $bucket, ''); + $rest->setParameter('location', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getBucketLocation({$bucket}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return (isset($rest->body[0]) && (string)$rest->body[0] !== '') ? (string)$rest->body[0] : 'US'; + } + + + /** + * Set object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param array $acp Access Control Policy Data (same as the data returned from getAccessControlPolicy) + * @return boolean + */ + public static function setAccessControlPolicy($bucket, $uri = '', $acp = array()) { + $dom = new DOMDocument; + $dom->formatOutput = true; + $accessControlPolicy = $dom->createElement('AccessControlPolicy'); + $accessControlList = $dom->createElement('AccessControlList'); + + // It seems the owner has to be passed along too + $owner = $dom->createElement('Owner'); + $owner->appendChild($dom->createElement('ID', $acp['owner']['id'])); + $owner->appendChild($dom->createElement('DisplayName', $acp['owner']['name'])); + $accessControlPolicy->appendChild($owner); + + foreach ($acp['acl'] as $g) { + $grant = $dom->createElement('Grant'); + $grantee = $dom->createElement('Grantee'); + $grantee->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); + if (isset($g['id'])) { // CanonicalUser (DisplayName is omitted) + $grantee->setAttribute('xsi:type', 'CanonicalUser'); + $grantee->appendChild($dom->createElement('ID', $g['id'])); + } elseif (isset($g['email'])) { // AmazonCustomerByEmail + $grantee->setAttribute('xsi:type', 'AmazonCustomerByEmail'); + $grantee->appendChild($dom->createElement('EmailAddress', $g['email'])); + } elseif ($g['type'] == 'Group') { // Group + $grantee->setAttribute('xsi:type', 'Group'); + $grantee->appendChild($dom->createElement('URI', $g['uri'])); + } + $grant->appendChild($grantee); + $grant->appendChild($dom->createElement('Permission', $g['permission'])); + $accessControlList->appendChild($grant); + } + + $accessControlPolicy->appendChild($accessControlList); + $dom->appendChild($accessControlPolicy); + + $rest = new S3Request('PUT', $bucket, $uri); + $rest->setParameter('acl', null); + $rest->data = $dom->saveXML(); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::setAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get object or bucket Access Control Policy + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed | false + */ + public static function getAccessControlPolicy($bucket, $uri = '') { + $rest = new S3Request('GET', $bucket, $uri); + $rest->setParameter('acl', null); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getAccessControlPolicy({$bucket}, {$uri}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + + $acp = array(); + if (isset($rest->body->Owner, $rest->body->Owner->ID, $rest->body->Owner->DisplayName)) { + $acp['owner'] = array( + 'id' => (string)$rest->body->Owner->ID, 'name' => (string)$rest->body->Owner->DisplayName + ); + } + if (isset($rest->body->AccessControlList)) { + $acp['acl'] = array(); + foreach ($rest->body->AccessControlList->Grant as $grant) { + foreach ($grant->Grantee as $grantee) { + if (isset($grantee->ID, $grantee->DisplayName)) // CanonicalUser + $acp['acl'][] = array( + 'type' => 'CanonicalUser', + 'id' => (string)$grantee->ID, + 'name' => (string)$grantee->DisplayName, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->EmailAddress)) // AmazonCustomerByEmail + $acp['acl'][] = array( + 'type' => 'AmazonCustomerByEmail', + 'email' => (string)$grantee->EmailAddress, + 'permission' => (string)$grant->Permission + ); + elseif (isset($grantee->URI)) // Group + $acp['acl'][] = array( + 'type' => 'Group', + 'uri' => (string)$grantee->URI, + 'permission' => (string)$grant->Permission + ); + else continue; + } + } + } + return $acp; + } + + + /** + * Delete an object + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return boolean + */ + public static function deleteObject($bucket, $uri) { + $rest = new S3Request('DELETE', $bucket, $uri); + $rest = $rest->getResponse(); + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteObject(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a query string authenticated URL + * + * @param string $bucket Bucket name + * @param string $uri Object URI + * @param integer $lifetime Lifetime in seconds + * @param boolean $hostBucket Use the bucket name as the hostname + * @param boolean $https Use HTTPS ($hostBucket should be false for SSL verification) + * @return string + */ + public static function getAuthenticatedURL($bucket, $uri, $lifetime, $hostBucket = false, $https = false) { + $expires = time() + $lifetime; + $uri = str_replace('%2F', '/', rawurlencode($uri)); // URI should be encoded (thanks Sean O'Dea) + return sprintf(($https ? 'https' : 'http').'://%s/%s?AWSAccessKeyId=%s&Expires=%u&Signature=%s', + $hostBucket ? $bucket : $bucket.'.s3.amazonaws.com', $uri, self::$__accessKey, $expires, + urlencode(self::__getHash("GET\n\n\n{$expires}\n/{$bucket}/{$uri}"))); + } + + /** + * Get upload POST parameters for form uploads + * + * @param string $bucket Bucket name + * @param string $uriPrefix Object URI prefix + * @param constant $acl ACL constant + * @param integer $lifetime Lifetime in seconds + * @param integer $maxFileSize Maximum filesize in bytes (default 5MB) + * @param string $successRedirect Redirect URL or 200 / 201 status code + * @param array $amzHeaders Array of x-amz-meta-* headers + * @param array $headers Array of request headers or content type as a string + * @param boolean $flashVars Includes additional "Filename" variable posted by Flash + * @return object + */ + public static function getHttpUploadPostParams($bucket, $uriPrefix = '', $acl = self::ACL_PRIVATE, $lifetime = 3600, $maxFileSize = 5242880, $successRedirect = "201", $amzHeaders = array(), $headers = array(), $flashVars = false) { + // Create policy object + $policy = new stdClass; + $policy->expiration = gmdate('Y-m-d\TH:i:s\Z', (time() + $lifetime)); + $policy->conditions = array(); + $obj = new stdClass; $obj->bucket = $bucket; array_push($policy->conditions, $obj); + $obj = new stdClass; $obj->acl = $acl; array_push($policy->conditions, $obj); + + $obj = new stdClass; // 200 for non-redirect uploads + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $obj->success_action_status = (string)$successRedirect; + else // URL + $obj->success_action_redirect = $successRedirect; + array_push($policy->conditions, $obj); + + array_push($policy->conditions, array('starts-with', '$key', $uriPrefix)); + if ($flashVars) array_push($policy->conditions, array('starts-with', '$Filename', '')); + foreach (array_keys($headers) as $headerKey) + array_push($policy->conditions, array('starts-with', '$'.$headerKey, '')); + foreach ($amzHeaders as $headerKey => $headerVal) { + $obj = new stdClass; $obj->{$headerKey} = (string)$headerVal; array_push($policy->conditions, $obj); + } + array_push($policy->conditions, array('content-length-range', 0, $maxFileSize)); + $policy = base64_encode(str_replace('\/', '/', json_encode($policy))); + + // Create parameters + $params = new stdClass; + $params->AWSAccessKeyId = self::$__accessKey; + $params->key = $uriPrefix.'${filename}'; + $params->acl = $acl; + $params->policy = $policy; unset($policy); + $params->signature = self::__getHash($params->policy); + if (is_numeric($successRedirect) && in_array((int)$successRedirect, array(200, 201))) + $params->success_action_status = (string)$successRedirect; + else + $params->success_action_redirect = $successRedirect; + foreach ($headers as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + foreach ($amzHeaders as $headerKey => $headerVal) $params->{$headerKey} = (string)$headerVal; + return $params; + } + + /** + * Create a CloudFront distribution + * + * @param string $bucket Bucket name + * @param boolean $enabled Enabled (true/false) + * @param array $cnames Array containing CNAME aliases + * @param string $comment Use the bucket name as the hostname + * @return array | false + */ + public static function createDistribution($bucket, $enabled = true, $cnames = array(), $comment = '') { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('POST', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($bucket.'.s3.amazonaws.com', $enabled, $comment, (string)microtime(true), $cnames); + $rest->size = strlen($rest->data); + $rest->setHeader('Content-Type', 'application/xml'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 201) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::createDistribution({$bucket}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) + return self::__parseCloudFrontDistributionConfig($rest->body); + return false; + } + + + /** + * Get CloudFront distribution info + * + * @param string $distributionId Distribution ID from listDistributions() + * @return array | false + */ + public static function getDistribution($distributionId) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution/'.$distributionId, 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::getDistribution($distributionId): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement) { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Update a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return array | false + */ + public static function updateDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('PUT', '', '2008-06-30/distribution/'.$dist['id'].'/config', 'cloudfront.amazonaws.com'); + $rest->data = self::__getCloudFrontDistributionConfigXML($dist['origin'], $dist['enabled'], $dist['comment'], $dist['callerReference'], $dist['cnames']); + $rest->size = strlen($rest->data); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::updateDistribution({$dist['id']}, ".(int)$enabled.", '$comment'): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } else { + $dist = self::__parseCloudFrontDistributionConfig($rest->body); + $dist['hash'] = $rest->headers['hash']; + return $dist; + } + return false; + } + + + /** + * Delete a CloudFront distribution + * + * @param array $dist Distribution array info identical to output of getDistribution() + * @return boolean + */ + public static function deleteDistribution($dist) { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('DELETE', '', '2008-06-30/distribution/'.$dist['id'], 'cloudfront.amazonaws.com'); + $rest->setHeader('If-Match', $dist['hash']); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 204) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::deleteDistribution({$dist['id']}): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } + return true; + } + + + /** + * Get a list of CloudFront distributions + * + * @return array + */ + public static function listDistributions() { + self::$useSSL = true; // CloudFront requires SSL + $rest = new S3Request('GET', '', '2008-06-30/distribution', 'cloudfront.amazonaws.com'); + $rest = self::__getCloudFrontResponse($rest); + + if ($rest->error === false && $rest->code !== 200) + $rest->error = array('code' => $rest->code, 'message' => 'Unexpected HTTP status'); + if ($rest->error !== false) { + trigger_error(sprintf("S3::listDistributions(): [%s] %s", + $rest->error['code'], $rest->error['message']), E_USER_WARNING); + return false; + } elseif ($rest->body instanceof SimpleXMLElement && isset($rest->body->DistributionSummary)) { + $list = array(); + if (isset($rest->body->Marker, $rest->body->MaxItems, $rest->body->IsTruncated)) { + //$info['marker'] = (string)$rest->body->Marker; + //$info['maxItems'] = (int)$rest->body->MaxItems; + //$info['isTruncated'] = (string)$rest->body->IsTruncated == 'true' ? true : false; + } + foreach ($rest->body->DistributionSummary as $summary) { + $list[(string)$summary->Id] = self::__parseCloudFrontDistributionConfig($summary); + } + return $list; + } + return array(); + } + + + /** + * Get a DistributionConfig DOMDocument + * + * @internal Used to create XML in createDistribution() and updateDistribution() + * @param string $bucket Origin bucket + * @param boolean $enabled Enabled (true/false) + * @param string $comment Comment to append + * @param string $callerReference Caller reference + * @param array $cnames Array of CNAME aliases + * @return string + */ + private static function __getCloudFrontDistributionConfigXML($bucket, $enabled, $comment, $callerReference = '0', $cnames = array()) { + $dom = new DOMDocument('1.0', 'UTF-8'); + $dom->formatOutput = true; + $distributionConfig = $dom->createElement('DistributionConfig'); + $distributionConfig->setAttribute('xmlns', 'http://cloudfront.amazonaws.com/doc/2008-06-30/'); + $distributionConfig->appendChild($dom->createElement('Origin', $bucket)); + $distributionConfig->appendChild($dom->createElement('CallerReference', $callerReference)); + foreach ($cnames as $cname) + $distributionConfig->appendChild($dom->createElement('CNAME', $cname)); + if ($comment !== '') $distributionConfig->appendChild($dom->createElement('Comment', $comment)); + $distributionConfig->appendChild($dom->createElement('Enabled', $enabled ? 'true' : 'false')); + $dom->appendChild($distributionConfig); + return $dom->saveXML(); + } + + + /** + * Parse a CloudFront distribution config + * + * @internal Used to parse the CloudFront DistributionConfig node to an array + * @param object &$node DOMNode + * @return array + */ + private static function __parseCloudFrontDistributionConfig(&$node) { + $dist = array(); + if (isset($node->Id, $node->Status, $node->LastModifiedTime, $node->DomainName)) { + $dist['id'] = (string)$node->Id; + $dist['status'] = (string)$node->Status; + $dist['time'] = strtotime((string)$node->LastModifiedTime); + $dist['domain'] = (string)$node->DomainName; + } + if (isset($node->CallerReference)) + $dist['callerReference'] = (string)$node->CallerReference; + if (isset($node->Comment)) + $dist['comment'] = (string)$node->Comment; + if (isset($node->Enabled, $node->Origin)) { + $dist['origin'] = (string)$node->Origin; + $dist['enabled'] = (string)$node->Enabled == 'true' ? true : false; + } elseif (isset($node->DistributionConfig)) { + $dist = array_merge($dist, self::__parseCloudFrontDistributionConfig($node->DistributionConfig)); + } + if (isset($node->CNAME)) { + $dist['cnames'] = array(); + foreach ($node->CNAME as $cname) $dist['cnames'][(string)$cname] = (string)$cname; + } + return $dist; + } + + + /** + * Grab CloudFront response + * + * @internal Used to parse the CloudFront S3Request::getResponse() output + * @param object &$rest S3Request instance + * @return object + */ + private static function __getCloudFrontResponse(&$rest) { + $rest->getResponse(); + if ($rest->response->error === false && isset($rest->response->body) && + is_string($rest->response->body) && substr($rest->response->body, 0, 5) == 'response->body = simplexml_load_string($rest->response->body); + // Grab CloudFront errors + if (isset($rest->response->body->Error, $rest->response->body->Error->Code, + $rest->response->body->Error->Message)) { + $rest->response->error = array( + 'code' => (string)$rest->response->body->Error->Code, + 'message' => (string)$rest->response->body->Error->Message + ); + unset($rest->response->body); + } + } + return $rest->response; + } + + + /** + * Get MIME type for file + * + * @internal Used to get mime types + * @param string &$file File path + * @return string + */ + public static function __getMimeType(&$file) { + $type = false; + // Fileinfo documentation says fileinfo_open() will use the + // MAGIC env var for the magic file + if (extension_loaded('fileinfo') && isset($_ENV['MAGIC']) && + ($finfo = finfo_open(FILEINFO_MIME, $_ENV['MAGIC'])) !== false) { + if (($type = finfo_file($finfo, $file)) !== false) { + // Remove the charset and grab the last content-type + $type = explode(' ', str_replace('; charset=', ';charset=', $type)); + $type = array_pop($type); + $type = explode(';', $type); + $type = trim(array_shift($type)); + } + finfo_close($finfo); + + // If anyone is still using mime_content_type() + } elseif (function_exists('mime_content_type')) + $type = trim(mime_content_type($file)); + + if ($type !== false && strlen($type) > 0) return $type; + + // Otherwise do it the old fashioned way + static $exts = array( + 'jpg' => 'image/jpeg', 'gif' => 'image/gif', 'png' => 'image/png', + 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'ico' => 'image/x-icon', + 'swf' => 'application/x-shockwave-flash', 'pdf' => 'application/pdf', + 'zip' => 'application/zip', 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', 'bz' => 'application/x-bzip', + 'bz2' => 'application/x-bzip2', 'txt' => 'text/plain', + 'asc' => 'text/plain', 'htm' => 'text/html', 'html' => 'text/html', + 'css' => 'text/css', 'js' => 'text/javascript', + 'xml' => 'text/xml', 'xsl' => 'application/xsl+xml', + 'ogg' => 'application/ogg', 'mp3' => 'audio/mpeg', 'wav' => 'audio/x-wav', + 'avi' => 'video/x-msvideo', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', + 'mov' => 'video/quicktime', 'flv' => 'video/x-flv', 'php' => 'text/x-php' + ); + $ext = strtolower(pathInfo($file, PATHINFO_EXTENSION)); + return isset($exts[$ext]) ? $exts[$ext] : 'application/octet-stream'; + } + + + /** + * Generate the auth string: "AWS AccessKey:Signature" + * + * @internal Used by S3Request::getResponse() + * @param string $string String to sign + * @return string + */ + public static function __getSignature($string) { + return 'AWS '.self::$__accessKey.':'.self::__getHash($string); + } + + + /** + * Creates a HMAC-SHA1 hash + * + * This uses the hash extension if loaded + * + * @internal Used by __getSignature() + * @param string $string String to sign + * @return string + */ + private static function __getHash($string) { + return base64_encode(extension_loaded('hash') ? + hash_hmac('sha1', $string, self::$__secretKey, true) : pack('H*', sha1( + (str_pad(self::$__secretKey, 64, chr(0x00)) ^ (str_repeat(chr(0x5c), 64))) . + pack('H*', sha1((str_pad(self::$__secretKey, 64, chr(0x00)) ^ + (str_repeat(chr(0x36), 64))) . $string))))); + } + +} + +final class S3Request { + private $verb, $bucket, $uri, $resource = '', $parameters = array(), + $amzHeaders = array(), $headers = array( + 'Host' => '', 'Date' => '', 'Content-MD5' => '', 'Content-Type' => '' + ); + public $fp = false, $size = 0, $data = false, $response; + + + /** + * Constructor + * + * @param string $verb Verb + * @param string $bucket Bucket name + * @param string $uri Object URI + * @return mixed + */ + function __construct($verb, $bucket = '', $uri = '', $defaultHost = 's3.amazonaws.com') { + $this->verb = $verb; + $this->bucket = strtolower($bucket); + $this->uri = $uri !== '' ? '/'.str_replace('%2F', '/', rawurlencode($uri)) : '/'; + + if ($this->bucket !== '') { + $this->headers['Host'] = $this->bucket.'.'.$defaultHost; + $this->resource = '/'.$this->bucket.$this->uri; + } else { + $this->headers['Host'] = $defaultHost; + //$this->resource = strlen($this->uri) > 1 ? '/'.$this->bucket.$this->uri : $this->uri; + $this->resource = $this->uri; + } + $this->headers['Date'] = gmdate('D, d M Y H:i:s T'); + + $this->response = new STDClass; + $this->response->error = false; + } + + + /** + * Set request parameter + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setParameter($key, $value) { + $this->parameters[$key] = $value; + } + + + /** + * Set request header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setHeader($key, $value) { + $this->headers[$key] = $value; + } + + + /** + * Set x-amz-meta-* header + * + * @param string $key Key + * @param string $value Value + * @return void + */ + public function setAmzHeader($key, $value) { + $this->amzHeaders[$key] = $value; + } + + + /** + * Get the S3 response + * + * @return object | false + */ + public function getResponse() { + $query = ''; + if (sizeof($this->parameters) > 0) { + $query = substr($this->uri, -1) !== '?' ? '?' : '&'; + foreach ($this->parameters as $var => $value) + if ($value == null || $value == '') $query .= $var.'&'; + // Parameters should be encoded (thanks Sean O'Dea) + else $query .= $var.'='.rawurlencode($value).'&'; + $query = substr($query, 0, -1); + $this->uri .= $query; + + if (array_key_exists('acl', $this->parameters) || + array_key_exists('location', $this->parameters) || + array_key_exists('torrent', $this->parameters) || + array_key_exists('logging', $this->parameters)) + $this->resource .= $query; + } + $url = ((S3::$useSSL && extension_loaded('openssl')) ? + 'https://':'http://').$this->headers['Host'].$this->uri; + //var_dump($this->bucket, $this->uri, $this->resource, $url); + + // Basic setup + $curl = curl_init(); + curl_setopt($curl, CURLOPT_USERAGENT, 'S3/php'); + + if (S3::$useSSL) { + curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 1); + curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 1); + } + + curl_setopt($curl, CURLOPT_URL, $url); + + // Headers + $headers = array(); $amz = array(); + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + foreach ($this->headers as $header => $value) + if (strlen($value) > 0) $headers[] = $header.': '.$value; + + // Collect AMZ headers for signature + foreach ($this->amzHeaders as $header => $value) + if (strlen($value) > 0) $amz[] = strtolower($header).':'.$value; + + // AMZ headers must be sorted + if (sizeof($amz) > 0) { + sort($amz); + $amz = "\n".implode("\n", $amz); + } else $amz = ''; + + // Authorization string (CloudFront stringToSign should only contain a date) + $headers[] = 'Authorization: ' . S3::__getSignature( + $this->headers['Host'] == 'cloudfront.amazonaws.com' ? $this->headers['Date'] : + $this->verb."\n".$this->headers['Content-MD5']."\n". + $this->headers['Content-Type']."\n".$this->headers['Date'].$amz."\n".$this->resource + ); + + curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, false); + curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback')); + curl_setopt($curl, CURLOPT_HEADERFUNCTION, array(&$this, '__responseHeaderCallback')); + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); + + // Request types + switch ($this->verb) { + case 'GET': break; + case 'PUT': case 'POST': // POST only used for CloudFront + if ($this->fp !== false) { + curl_setopt($curl, CURLOPT_PUT, true); + curl_setopt($curl, CURLOPT_INFILE, $this->fp); + if ($this->size >= 0) + curl_setopt($curl, CURLOPT_INFILESIZE, $this->size); + } elseif ($this->data !== false) { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + curl_setopt($curl, CURLOPT_POSTFIELDS, $this->data); + if ($this->size >= 0) + curl_setopt($curl, CURLOPT_BUFFERSIZE, $this->size); + } else + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb); + break; + case 'HEAD': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); + curl_setopt($curl, CURLOPT_NOBODY, true); + break; + case 'DELETE': + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + break; + default: break; + } + + // Execute, grab errors + if (curl_exec($curl)) + $this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE); + else + $this->response->error = array( + 'code' => curl_errno($curl), + 'message' => curl_error($curl), + 'resource' => $this->resource + ); + + @curl_close($curl); + + // Parse body into XML + if ($this->response->error === false && isset($this->response->headers['type']) && + $this->response->headers['type'] == 'application/xml' && isset($this->response->body)) { + $this->response->body = simplexml_load_string($this->response->body); + + // Grab S3 errors + if (!in_array($this->response->code, array(200, 204)) && + isset($this->response->body->Code, $this->response->body->Message)) { + $this->response->error = array( + 'code' => (string)$this->response->body->Code, + 'message' => (string)$this->response->body->Message + ); + if (isset($this->response->body->Resource)) + $this->response->error['resource'] = (string)$this->response->body->Resource; + unset($this->response->body); + } + } + + // Clean up file resources + if ($this->fp !== false && is_resource($this->fp)) fclose($this->fp); + + return $this->response; + } + + + /** + * CURL write callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseWriteCallback(&$curl, &$data) { + if ($this->response->code == 200 && $this->fp !== false) + return fwrite($this->fp, $data); + else + $this->response->body .= $data; + return strlen($data); + } + + + /** + * CURL header callback + * + * @param resource &$curl CURL resource + * @param string &$data Data + * @return integer + */ + private function __responseHeaderCallback(&$curl, &$data) { + if (($strlen = strlen($data)) <= 2) return $strlen; + if (substr($data, 0, 4) == 'HTTP') + $this->response->code = (int)substr($data, 9, 3); + else { + list($header, $value) = explode(': ', trim($data), 2); + if ($header == 'Last-Modified') + $this->response->headers['time'] = strtotime($value); + elseif ($header == 'Content-Length') + $this->response->headers['size'] = (int)$value; + elseif ($header == 'Content-Type') + $this->response->headers['type'] = $value; + elseif ($header == 'ETag') + $this->response->headers['hash'] = $value{0} == '"' ? substr($value, 1, -1) : $value; + elseif (preg_match('/^x-amz-meta-.*$/', $header)) + $this->response->headers[$header] = is_numeric($value) ? (int)$value : $value; + } + return $strlen; + } + +} diff --git a/3.0/modules/aws_s3/models/MY_Item_Model.php b/3.0/modules/aws_s3/models/MY_Item_Model.php new file mode 100644 index 00000000..4cacc2fc --- /dev/null +++ b/3.0/modules/aws_s3/models/MY_Item_Model.php @@ -0,0 +1,40 @@ +is_photo()) { + return aws_s3::generate_url("th/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + else if ($this->is_album() && $this->id > 1) { + return aws_s3::generate_url("th/" . $this->relative_path() . "/.album.jpg", ($this->view_1 == 1 ? false : true), $this->updated); + } + else if ($this->is_movie()) { + $relative_path = preg_replace("/...$/", "jpg", $this->relative_path()); + return aws_s3::generate_url("th/" . $relative_path, ($this->view_1 == 1 ? false : true), $this->updated); + } + } + + public function file_url($full_uri=false) { + if (!module::get_var("aws_s3", "enabled")) + return parent::file_url($full_uri); + + return aws_s3::generate_url("fs/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + + public function resize_url($full_uri=false) { + if (!module::get_var("aws_s3", "enabled")) + return parent::resize_url($full_uri); + + if ($this->is_album() && $this->id > 1) { + return aws_s3::generate_url("rs/" . $this->relative_path() . "/.album.jpg", ($this->view_1 == 1 ? false : true), $this->updated); + } + else { + return aws_s3::generate_url("rs/" . $this->relative_path(), ($this->view_1 == 1 ? false : true), $this->updated); + } + } + +} \ No newline at end of file diff --git a/3.0/modules/aws_s3/module.info b/3.0/modules/aws_s3/module.info new file mode 100644 index 00000000..6c301507 --- /dev/null +++ b/3.0/modules/aws_s3/module.info @@ -0,0 +1,3 @@ +name = "Amazon S3" +description = "Seamlessly transfer your Gallery data to Amazon S3 CDN for a lightning fast gallery" +version = 1 diff --git a/3.0/modules/aws_s3/views/admin_aws_s3.html.php b/3.0/modules/aws_s3/views/admin_aws_s3.html.php new file mode 100644 index 00000000..961186e3 --- /dev/null +++ b/3.0/modules/aws_s3/views/admin_aws_s3.html.php @@ -0,0 +1,13 @@ + + +
                        +

                        + +

                        + +
                        + +
                        +
                        + + From 565e616909d4cadcaf17512ff19ef75c20203d1f Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sun, 28 Nov 2010 19:59:40 +0800 Subject: [PATCH 124/300] added aws_s3 module --- .../aws_s3/helpers/MY_embedlinks_block.php | 20 ++++++++++++++++++- .../aws_s3/helpers/MY_embedlinks_theme.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/MY_item.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3_event.php | 20 ++++++++++++++++++- .../aws_s3/helpers/aws_s3_installer.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/helpers/aws_s3_task.php | 20 ++++++++++++++++++- 3.0/modules/aws_s3/models/MY_Item_Model.php | 20 ++++++++++++++++++- 8 files changed, 152 insertions(+), 8 deletions(-) diff --git a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php index fe251b1c..e9ca1611 100644 --- a/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php +++ b/3.0/modules/aws_s3/helpers/MY_embedlinks_block.php @@ -1,4 +1,22 @@ - Date: Sun, 28 Nov 2010 19:22:01 +0800 Subject: [PATCH 125/300] Comments --- 3.0/modules/user_chroot/helpers/user_chroot_installer.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index ee89fb13..f12a2ed1 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -19,6 +19,9 @@ */ class user_chroot_installer { + /** + * Create the table user_chroot when installing the module. + */ static function install() { $db = Database::instance(); $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( @@ -31,7 +34,7 @@ class user_chroot_installer { } /** - * Drops the table of user chroot when the module is uninstalled. + * Drops the table user_chroot when uninstalling the module. */ static function uninstall() { $db = Database::instance(); From a23cf114dc98acb0207b983f75a017b160511629 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 19:18:42 +0800 Subject: [PATCH 126/300] - Code cleanup - Fix some bugs - Serious performances improvement when building the tree (regression: tree is not alphabetically ordered) --- .../user_chroot/helpers/user_chroot_event.php | 122 +++++++++--------- 1 file changed, 62 insertions(+), 60 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_event.php b/3.0/modules/user_chroot/helpers/user_chroot_event.php index c43c6dc4..51027a44 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_event.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_event.php @@ -21,92 +21,94 @@ class user_chroot_event_Core { /** - * Called just before a user is deleted. This will remove the user from - * the user_chroot directory. + * Called just before a user deletion. */ - static function user_before_delete($user) { - ORM::factory("user_chroot")->delete($user->id); + public static function user_before_delete($user) { + ORM::factory('user_chroot', $user->id)->delete(); } /** - * Called when admin is adding a user + * Called just before an item deletion. + */ + public static function item_before_delete($item) { + if( $item->is_album() ) { + ORM::factory('user_chroot')->where('album_id', '=', $item->id)->delete(); + } + } + + /** + * Called when building the 'Add user' form for an admin. */ static function user_add_form_admin($user, $form) { - $form->add_user->dropdown("user_chroot") + $form->add_user->dropdown('user_chroot') ->label(t("Root Album")) - ->options(self::createGalleryArray()) - ->selected(0); + ->options(self::albumsTreeArray()) + ->selected(1); } /** - * Called after a user has been added + * Called just after a user has been added by an admin. */ - static function user_add_form_admin_completed($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - $user_chroot->id = $user->id; - $user_chroot->album_id = $form->add_user->user_chroot->value; - $user_chroot->save(); - } - - /** - * Called when admin is editing a user - */ - static function user_edit_form_admin($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - if ($user_chroot->loaded()) { - $selected = $user_chroot->album_id; - } else { - $selected = 0; + public static function user_add_form_admin_completed($user, $form) { + if( $form->add_user->user_chroot->value > 1 ) { + $user_chroot = ORM::factory('user_chroot'); + $user_chroot->id = $user->id; + $user_chroot->album_id = $form->add_user->user_chroot->value; + $user_chroot->save(); } - $form->edit_user->dropdown("user_chroot") + } + + /** + * Called when building the 'Edit user' form for an admin. + */ + public static function user_edit_form_admin($user, $form) { + $user_chroot = ORM::factory('user_chroot', $user->id); + + $selected = ( $user_chroot->loaded() ) + ? $user_chroot->album_id + : 1; + + $form->edit_user->dropdown('user_chroot') ->label(t("Root Album")) - ->options(self::createGalleryArray()) + ->options(self::albumsTreeArray()) ->selected($selected); } /** - * Called after a user had been edited by the admin + * Called just after a user has been edited by an admin. */ - static function user_edit_form_admin_completed($user, $form) { - $user_chroot = ORM::factory("user_chroot")->where("id", "=", $user->id)->find(); - if ($user_chroot->loaded()) { - $user_chroot->album_id = $form->edit_user->user_chroot->value; + public static function user_edit_form_admin_completed($user, $form) { + if( $form->edit_user->user_chroot->value <= 1 ) { + ORM::factory('user_chroot')->delete($user->id); + } else { - $user_chroot->id = $user->id; + $user_chroot = ORM::factory('user_chroot', $user->id); + + if( !$user_chroot->loaded() ) { + $user_chroot = ORM::factory('user_chroot'); + $user_chroot->id = $user->id; + } + $user_chroot->album_id = $form->edit_user->user_chroot->value; + $user_chroot->save(); } - $user_chroot->save(); - } - - - /** - * Creates an array of galleries - */ - static function createGalleryArray() { - $array[0] = "none"; - $root = ORM::factory("item", 1); - self::tree($root, "", $array); - return $array; } /** - * recursive function to build array for drop down list + * Generate an array representing the hierarchy of albums. */ - static function tree($parent, $dashes, &$array) { - if ($parent->id == "1") { - $array[$parent->id] = ORM::factory("item", 1)->title; - } else { - $array[$parent->id] = "$dashes $parent->name"; - } - - $albums = ORM::factory("item") - ->where("parent_id", "=", $parent->id) - ->where("type", "=", "album") - ->order_by("title", "ASC") + private static function albumsTreeArray($level_marker = '    ') { + $tree = array(); + $albums = ORM::factory('item') + ->where('type', '=', 'album') + ->order_by('left_ptr', 'ASC') ->find_all(); - foreach ($albums as $album) { - self::tree($album, "-$dashes", $array); + + foreach($albums as $album) { + $tree[$album->id] = html::clean( + str_repeat($level_marker, $album->level - 1).' '.$album->title ); } - return; + + return $tree; } } From a4ba3012e7735c5e29051784c66ad3b616ff228a Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:03:57 +0800 Subject: [PATCH 127/300] - Overload a forgotten method - Code cleanup --- 3.0/modules/user_chroot/helpers/MY_item.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_item.php b/3.0/modules/user_chroot/helpers/MY_item.php index 423b8ddc..d6368f18 100644 --- a/3.0/modules/user_chroot/helpers/MY_item.php +++ b/3.0/modules/user_chroot/helpers/MY_item.php @@ -24,11 +24,17 @@ class item extends item_Core { if( user_chroot::album() ) { $model->and_open() - ->and_where("items.left_ptr", ">=", user_chroot::album()->left_ptr) - ->and_where("items.right_ptr", "<=", user_chroot::album()->right_ptr) + ->and_where('items.left_ptr', '>=', user_chroot::album()->left_ptr) + ->and_where('items.right_ptr', '<=', user_chroot::album()->right_ptr) ->close(); } return $model; } + + static function root() { + return ( user_chroot::album() ) + ? user_chroot::album() + : parent::root(); + } } From 52bef7234d0e6f7557f495e3f03137d295d49965 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 19:23:35 +0800 Subject: [PATCH 128/300] Explicit is better than implicit... --- 3.0/modules/user_chroot/helpers/user_chroot_installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index f12a2ed1..f5f597c4 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -22,7 +22,7 @@ class user_chroot_installer { /** * Create the table user_chroot when installing the module. */ - static function install() { + public static function install() { $db = Database::instance(); $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( `id` int(9) NOT NULL, @@ -36,7 +36,7 @@ class user_chroot_installer { /** * Drops the table user_chroot when uninstalling the module. */ - static function uninstall() { + public static function uninstall() { $db = Database::instance(); $db->query("DROP TABLE IF EXISTS {user_chroots};"); } From 071f3f383167eed6d8a68219c19484b8c895b0ce Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 19:37:57 +0800 Subject: [PATCH 129/300] - Code cleanup - Comments --- 3.0/modules/user_chroot/helpers/user_chroot.php | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot.php b/3.0/modules/user_chroot/helpers/user_chroot.php index 4d0a8499..8afa753f 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot.php +++ b/3.0/modules/user_chroot/helpers/user_chroot.php @@ -21,18 +21,21 @@ class user_chroot_Core { private static $_album = null; + /** + * Return the root album of the current user, or false if the user is not + * chrooted. + */ public static function album() { if( is_null(self::$_album) ) { self::$_album = false; - $user = identity::active_user(); + $item = ORM::factory('item') + ->join('user_chroots', 'items.id', 'user_chroots.album_id') + ->where('user_chroots.id', '=', identity::active_user()->id) + ->find(); - $user_chroot = ORM::factory("user_chroot", $user->id); - if( $user_chroot->loaded() && $user_chroot->album_id != 0 ) { - $item = ORM::factory("item", $user_chroot->album_id); - if( $item->loaded() ) { - self::$_album = $item; - } + if( $item->loaded() ) { + self::$_album = $item; } } From e04c384fbd0955069805a1f3bd1c423b64aca22f Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:04:59 +0800 Subject: [PATCH 130/300] - Bugfix - Comments - Code cleanup --- 3.0/modules/user_chroot/helpers/MY_url.php | 29 ++++++++++++++++------ 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_url.php b/3.0/modules/user_chroot/helpers/MY_url.php index 31818fa4..b693c9c5 100644 --- a/3.0/modules/user_chroot/helpers/MY_url.php +++ b/3.0/modules/user_chroot/helpers/MY_url.php @@ -18,25 +18,40 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -// /!\ Hack: There is no good way to extend gallery url +/* + * /!\ Hack + * Module 'gallery' already provides a class 'url' which extends url_Core. This + * hack renames class 'url', so user_chroot can have its own (which extends the + * original) + */ $gallery_url = file_get_contents(MODPATH . 'gallery/helpers/MY_url.php'); -$gallery_url = preg_replace('#^<\?php #', '', $gallery_url); -$gallery_url = preg_replace('#class url extends url_Core#', 'class url_G3 extends url_Core', $gallery_url); +$gallery_url = str_replace('relative_url().'/'.Router::$current_uri, '/'); - Router::$current_uri = trim(user_chroot::album()->relative_url().'/'.Router::$current_uri, '/'); + } else if( is_null(Router::$controller) && Router::$current_uri != '' ) { + // Non-root album requested + Router::$current_uri = trim(user_chroot::album()->relative_url().'/'.Router::$current_uri, '/'); + } } return parent::parse_url(); } + /** + * Remove the chroot part of the URI. + */ static function site($uri = '', $protocol = FALSE) { if( user_chroot::album() ) { $uri = preg_replace('#^'.user_chroot::album()->relative_url().'#', '', $uri); From fcf8ecca1d749698e369f381b610370c94ef81b1 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:09:18 +0800 Subject: [PATCH 131/300] Comments --- 3.0/modules/user_chroot/helpers/MY_access.php | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/MY_access.php b/3.0/modules/user_chroot/helpers/MY_access.php index 5f73b4ca..e1a854f8 100644 --- a/3.0/modules/user_chroot/helpers/MY_access.php +++ b/3.0/modules/user_chroot/helpers/MY_access.php @@ -21,22 +21,27 @@ class access extends access_Core { /** - * Does the active user have this permission on this item? - * - * @param string $perm_name - * @param Item_Model $item - * @return boolean + * If the user is chrooted, deny access outside of the chroot. + */ + static function user_can($user, $perm_name, $item) { + if( $user->id == identity::active_user()->id && user_chroot::album() ) { + if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { + return false; + } + } + + return parent::user_can($user, $perm_name, $item); + } + + /** + * Copied from modules/gallery/helpers/access.php because of the usage of self:: */ static function can($perm_name, $item) { return self::user_can(identity::active_user(), $perm_name, $item); } /** - * If the active user does not have this permission, failed with an access::forbidden(). - * - * @param string $perm_name - * @param Item_Model $item - * @return boolean + * Copied from modules/gallery/helpers/access.php because of the usage of self:: */ static function required($perm_name, $item) { if (!self::can($perm_name, $item)) { @@ -48,14 +53,4 @@ class access extends access_Core { } } } - - static function user_can($user, $perm_name, $item) { - if( $user->id == identity::active_user()->id && user_chroot::album() ) { - if( $item->left_ptr < user_chroot::album()->left_ptr || user_chroot::album()->right_ptr < $item->right_ptr ) { - return false; - } - } - - return parent::user_can($user, $perm_name, $item); - } } From 01158eb356dff72445c73dbeae36b3953db860a1 Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sun, 28 Nov 2010 23:15:56 +0800 Subject: [PATCH 132/300] Comments and code cleanup Overload ORM_MPTT::parent() --- .../helpers/user_chroot_installer.php | 8 ++++---- .../user_chroot/libraries/MY_ORM_MPTT.php | 20 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/3.0/modules/user_chroot/helpers/user_chroot_installer.php b/3.0/modules/user_chroot/helpers/user_chroot_installer.php index f5f597c4..dd92fdf0 100644 --- a/3.0/modules/user_chroot/helpers/user_chroot_installer.php +++ b/3.0/modules/user_chroot/helpers/user_chroot_installer.php @@ -24,13 +24,13 @@ class user_chroot_installer { */ public static function install() { $db = Database::instance(); - $db->query("CREATE TABLE IF NOT EXISTS {user_chroots} ( + $db->query('CREATE TABLE IF NOT EXISTS {user_chroots} ( `id` int(9) NOT NULL, `album_id` int(9) default NULL, PRIMARY KEY (`id`), UNIQUE KEY(`id`)) - DEFAULT CHARSET=utf8;"); - module::set_version("user_chroot", 1); + DEFAULT CHARSET=utf8;'); + module::set_version('user_chroot', 1); } /** @@ -38,6 +38,6 @@ class user_chroot_installer { */ public static function uninstall() { $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {user_chroots};"); + $db->query('DROP TABLE IF EXISTS {user_chroots};'); } } diff --git a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php index e31124ca..767d2645 100644 --- a/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php +++ b/3.0/modules/user_chroot/libraries/MY_ORM_MPTT.php @@ -19,8 +19,10 @@ */ class ORM_MPTT extends ORM_MPTT_Core { + /** + * Copied from modules/gallery/libraries/ORM_MPTT.php, not sure of the reason... + */ private $model_name = null; - function __construct($id=null) { parent::__construct($id); $this->model_name = inflector::singular($this->table_name); @@ -31,13 +33,13 @@ class ORM_MPTT extends ORM_MPTT_Core { * * @return ORM */ - /*function parent() { + function parent() { if( user_chroot::album() && user_chroot::album()->id == $this->id ) { return null; } else { return parent::parent(); } - }*/ + } /** * Return all the parents of this node, in order from root to this node's immediate parent. @@ -46,14 +48,14 @@ class ORM_MPTT extends ORM_MPTT_Core { */ function parents() { $select = $this - ->where("left_ptr", "<=", $this->left_ptr) - ->where("right_ptr", ">=", $this->right_ptr) - ->where("id", "<>", $this->id) - ->order_by("left_ptr", "ASC"); + ->where('left_ptr', '<=', $this->left_ptr) + ->where('right_ptr', '>=', $this->right_ptr) + ->where('id', '<>', $this->id) + ->order_by('left_ptr', 'ASC'); if( user_chroot::album() ) { - $select->where("left_ptr", ">=", user_chroot::album()->left_ptr); - $select->where("right_ptr", "<=", user_chroot::album()->right_ptr); + $select->where('left_ptr', '>=', user_chroot::album()->left_ptr); + $select->where('right_ptr', '<=', user_chroot::album()->right_ptr); } return $select->find_all(); From a02d15e4ee93a73cb8fab6fb2e738e3989a2c360 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Thu, 16 Dec 2010 17:16:24 +0100 Subject: [PATCH 133/300] Optimizations. Still fairly ugly code, but it's quicker ugly code now. --- 3.0/modules/unrest/helpers/unrest_rest.php | 48 ++++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/3.0/modules/unrest/helpers/unrest_rest.php b/3.0/modules/unrest/helpers/unrest_rest.php index c16d4d4f..3e82a47c 100644 --- a/3.0/modules/unrest/helpers/unrest_rest.php +++ b/3.0/modules/unrest/helpers/unrest_rest.php @@ -1,14 +1,14 @@ 'name', @@ -22,7 +22,7 @@ class unrest_rest_Core { return $limit; } - private function getBasicLimiters($request, $limit = array()) + private static function getBasicLimiters($request, $limit = array()) { $directMapping = array( 'type' => 'type', @@ -37,7 +37,7 @@ class unrest_rest_Core { return $limit; } - private function albumsICanAccess() + private static function albumsICanAccess() { $db = db::build(); $gids = identity::group_ids_for_active_user(); @@ -136,7 +136,7 @@ class unrest_rest_Core { } } - static function addChildren($request, $db, $filler, $permitted, $display, &$return) + static function addChildren($request, $db, $filler, $permitted, $display, &$return, $rest_base) { $children = $db->select('parent_id', 'id')->from('items')->where('parent_id', 'IN', $filler['children_of']); if (isset($request->params->childtypes)) @@ -165,7 +165,7 @@ class unrest_rest_Core { else { $members = array(); foreach ($childBlock[ $data['entity']['id'] ] as $child) { - $members[] = unrest_rest::makeRestURL('item', $child); + $members[] = unrest_rest::makeRestURL('item', $child, $rest_base); } $data['members'] = $members; } @@ -177,13 +177,13 @@ class unrest_rest_Core { } } - private function makeRestURL($resource, $identifier) + private static function makeRestURL($resource, $identifier, $base) { - return url::abs_site("rest") . '/' . $resource . '/' . $identifier; + return $base . '/' . $resource . '/' . $identifier; } - public function size_url($size, $relative_path_cache, $type) { - $base = url::abs_file('var/' . $size . '/' ) . $relative_path_cache; + public static function size_url($size, $relative_path_cache, $type, $file_base) { + $base = $file_base . 'var/' . $size . '/' . $relative_path_cache; if ($type == 'photo') { return $base; } else if ($type == 'album') { @@ -193,17 +193,18 @@ class unrest_rest_Core { return preg_replace("/...$/", "jpg", $base); } } - - + + static function get($request) { $db = db::build(); + $start = microtime(true); + $rest_base = url::abs_site("rest"); + $file_base = url::abs_file(''); #'var/' . $size . '/' /* Build basic limiters */ $limit = unrest_rest::getBasicLimiters($request); $limit = unrest_rest::getFreetextLimiters($request,$limit); - error_log(print_r($limit,1)); - /* Build numeric limiters */ /* ...at some point. */ @@ -257,21 +258,21 @@ class unrest_rest_Core { 'thumb_width' => $item->resize_width ); - $ui['thumb_url_public'] = unrest_rest::size_url('thumbs', $item->relative_path_cache, $item->type); + $ui['thumb_url_public'] = unrest_rest::size_url('thumbs', $item->relative_path_cache, $item->type, $file_base); $public = $item->view_1?true:false; $fullPublic = $item->view_full_1?true:false; if ($item->type != 'album') { - $ui['file_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=full'); - $ui['thumb_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=thumb'); - $ui['resize_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=resize'); + $ui['file_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=full', $rest_base); + $ui['thumb_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=thumb', $rest_base); + $ui['resize_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=resize', $rest_base); if ($public) { - $ui['resize_url_public'] = unrest_rest::size_url('resizes', $item->relative_path_cache, $item->type); + $ui['resize_url_public'] = unrest_rest::size_url('resizes', $item->relative_path_cache, $item->type, $file_base); if ($fullPublic) { - $ui['file_url_public'] = unrest_rest::size_url('albums', $item->relative_path_cache, $item->type); + $ui['file_url_public'] = unrest_rest::size_url('albums', $item->relative_path_cache, $item->type, $file_base); } } } @@ -284,7 +285,7 @@ class unrest_rest_Core { } $return[] = array( - 'url' => unrest_rest::makeRestURL('item', $item->id ), + 'url' => unrest_rest::makeRestURL('item', $item->id, $rest_base ), 'entity' => $data ); } @@ -293,9 +294,12 @@ class unrest_rest_Core { /* Do we need to fetch children? */ if (array_key_exists('children_of', $filler)) { - unrest_rest::addChildren($request, $db, $filler, $permitted, $display, &$return); + unrest_rest::addChildren($request, $db, $filler, $permitted, $display, &$return, $rest_base); } + $end = microtime(true); + error_log("Inner " . ($end - $start) . " seconds taken"); + return $return; } } From d05320022280f2c5a6b2759b78f9818206f21dc5 Mon Sep 17 00:00:00 2001 From: danneh3826 Date: Sun, 12 Dec 2010 21:30:34 +0800 Subject: [PATCH 134/300] fixed hard coded bucket name in aws_s3 helper --- 3.0/modules/aws_s3/helpers/aws_s3.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/modules/aws_s3/helpers/aws_s3.php b/3.0/modules/aws_s3/helpers/aws_s3.php index 9b5b5eb5..891ba0d3 100644 --- a/3.0/modules/aws_s3/helpers/aws_s3.php +++ b/3.0/modules/aws_s3/helpers/aws_s3.php @@ -53,7 +53,7 @@ class aws_s3_Core { "&Signature=" . urlencode(self::getHash("GET\n\n\n" . (time() + module::get_var("aws_s3", "sig_exp")) . "\n/" . $host . "/" . $resource)); self::get_s3(); - S3::getAuthenticatedURL("danneh-org", $resource, module::get_var("aws_s3", "sig_exp")); + S3::getAuthenticatedURL(module::get_var("aws_s3", "bucket_name"), $resource, module::get_var("aws_s3", "sig_exp")); } else $url .= "?m=" . ($updated ? $updated : time()); From 1aefb007dfaa2b8685c7c4598653a359fdb63b8c Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Mon, 6 Dec 2010 02:27:13 +0800 Subject: [PATCH 135/300] Very, very basic and preliminary rest query support.. --- 3.0/modules/unrest/helpers/unrest.php | 22 ++ 3.0/modules/unrest/helpers/unrest_event.php | 8 + 3.0/modules/unrest/helpers/unrest_rest.php | 304 ++++++++++++++++++++ 3.0/modules/unrest/module.info | 3 + 4 files changed, 337 insertions(+) create mode 100644 3.0/modules/unrest/helpers/unrest.php create mode 100644 3.0/modules/unrest/helpers/unrest_event.php create mode 100644 3.0/modules/unrest/helpers/unrest_rest.php create mode 100644 3.0/modules/unrest/module.info diff --git a/3.0/modules/unrest/helpers/unrest.php b/3.0/modules/unrest/helpers/unrest.php new file mode 100644 index 00000000..a132b536 --- /dev/null +++ b/3.0/modules/unrest/helpers/unrest.php @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/3.0/modules/unrest/helpers/unrest_rest.php b/3.0/modules/unrest/helpers/unrest_rest.php new file mode 100644 index 00000000..c16d4d4f --- /dev/null +++ b/3.0/modules/unrest/helpers/unrest_rest.php @@ -0,0 +1,304 @@ + 'name', + 'description' => 'description' + ); + foreach ($likeMapping as $key => $col) + { + if (isset($request->params->$key)) { $limit[$col] = array('op' => 'LIKE', 'value' => '%' . $request->params->$key . '%'); } + } + + return $limit; + } + + private function getBasicLimiters($request, $limit = array()) + { + $directMapping = array( + 'type' => 'type', + 'id' => 'items.id', + 'parent' => 'parent_id', + 'mime' => 'mime_type'); + foreach ($directMapping as $key => $col) + { + if (isset($request->params->$key)) { $limit[$col] = array('op' => '=', 'value' => unrest_rest::resolveLimitOption($request->params->$key)); } + } + + return $limit; + } + + private function albumsICanAccess() + { + $db = db::build(); + $gids = identity::group_ids_for_active_user(); + $q = $db->select('id')->from('items'); + foreach ($gids as $gid) { $q->or_where('view_' . $gid, '=', 1); } + + $q->where('type', '=', 'album'); + $permitted = array(); + foreach($q->execute() as $row) { $permitted[] = $row->id; } + + return $permitted; + } + + static function queryLimitByPermission(&$query, $permitted) + { + $query->and_open()->and_open()->where('type', '=', 'album')->and_where('items.id', 'IN', $permitted)->close(); + $query->or_open()->where('type', '!=', 'album')->and_where('parent_id', 'IN', $permitted)->close()->close(); + } + + static function baseItemQuery($db) + { + $fields = array( + 'items.id', 'title', 'album_cover_item_id', 'description', 'height', 'width', 'left_ptr', 'right_ptr', + 'level', 'mime_type', 'name', 'owner_id', 'parent_id', 'relative_path_cache', 'relative_url_cache', + 'resize_dirty', 'slug', 'sort_column', 'sort_order', 'thumb_dirty','thumb_height', 'view_1', 'type', + 'resize_height', 'resize_width', 'thumb_height', 'thumb_width', 'slug', 'name', 'relative_path_cache' + ); + + $permfields = array('view_', 'view_full_', 'edit_', 'add_'); + + foreach (identity::group_ids_for_active_user() as $album) + { + foreach ($permfields as $field) + { + $fields[] = $field . $album; + } + } + + return($db->select($fields)->from('items')->join('access_caches', 'access_caches.item_id', 'items.id')); +/* + return($db->select(array( + 'id', 'title', 'album_cover_item_id', 'description', 'height', 'width', 'left_ptr', 'right_ptr', + 'level', 'mime_type', 'name', 'owner_id', 'parent_id', 'relative_path_cache', 'relative_url_cache', + 'resize_dirty', 'slug', 'sort_column', 'sort_order', 'thumb_dirty','thumb_height', 'view_1', 'type', + 'resize_height', 'resize_width', 'thumb_height', 'thumb_width', 'slug', 'name', 'relative_path_cache' + ))->from('items')); + */ + } + + static function queryLimitByLimiter(&$query, $limit) + { + foreach ($limit as $key => $block) + { + if (gettype($block['value']) == 'array') { $query->and_where($key, 'IN', $block['value']); } + else { $query->and_where($key, $block['op'], $block['value']); } + } + } + + static function getDisplayOptions($request) + { + if (isset($request->params->display)) { + return(split(',', $request->params->display)); + } else { + return(array('uiimage','uitext','ownership','members')); + }; + } + + static function queryOrder(&$query, $request) + { + if (isset($request->params->order)) { + $order = $request->params->order; + $direction = 'asc'; + if (isset($request->params->direction)) + { + if ($request->params->direction == 'desc') { $direction = 'desc'; } + } + + switch ($order) + { + case 'tree': + $query->order_by(array('level' => 'ASC', 'left_ptr' => 'ASC')); + break; + case 'created': + $query->order_by(array('created' => $direction)); + break; + case 'updated': + $query->order_by(array('updated' => $direction)); + break; + case 'views': + $query->order_by(array('view_count' => $direction)); + break; + case 'type': + $query->order_by(array('type' => $direction)); + break; + } + } + } + + static function addChildren($request, $db, $filler, $permitted, $display, &$return) + { + $children = $db->select('parent_id', 'id')->from('items')->where('parent_id', 'IN', $filler['children_of']); + if (isset($request->params->childtypes)) + { + $types = split(',', $request->params->childtypes); + $children->where('type', 'IN', $types); + } + + /* We shouldn't have any albums we don't have access to by default in this query, but just in case.. */ + unrest_rest::queryLimitByPermission(&$children, $permitted); + + + $childBlock = array(); + foreach($children->execute() as $item) + { + $childBlock[$item->parent_id][] = intval($item->id); + } + + foreach ($return as &$data) + { + if (array_key_exists($data['entity']['id'], $childBlock)) + { + if (in_array('terse', $display)) { + $data['members'] = $childBlock[ $data['id'] ]; + } + else { + $members = array(); + foreach ($childBlock[ $data['entity']['id'] ] as $child) { + $members[] = unrest_rest::makeRestURL('item', $child); + } + $data['members'] = $members; + } + } + else + { + $data['members'] = array(); + } + } + } + + private function makeRestURL($resource, $identifier) + { + return url::abs_site("rest") . '/' . $resource . '/' . $identifier; + } + + public function size_url($size, $relative_path_cache, $type) { + $base = url::abs_file('var/' . $size . '/' ) . $relative_path_cache; + if ($type == 'photo') { + return $base; + } else if ($type == 'album') { + return $base . "/.album.jpg"; + } else if ($type == 'movie') { + // Replace the extension with jpg + return preg_replace("/...$/", "jpg", $base); + } + } + + + static function get($request) { + $db = db::build(); + + /* Build basic limiters */ + $limit = unrest_rest::getBasicLimiters($request); + $limit = unrest_rest::getFreetextLimiters($request,$limit); + + error_log(print_r($limit,1)); + + /* Build numeric limiters */ + /* ...at some point. */ + + /* Figure out an array of albums we got permissions to access */ + $permitted = unrest_rest::albumsICanAccess(); + + $display = unrest_rest::getDisplayOptions($request); + $items = unrest_rest::baseItemQuery($db); + + /* + Introduce some WHERE statements that'll make sure that we don't get to see stuff we + shouldn't be seeing. + */ + unrest_rest::queryLimitByPermission(&$items, $permitted); + unrest_rest::queryLimitByLimiter(&$items, $limit); + unrest_rest::queryOrder(&$items, $request); + + $return = array(); + $filler = array(); + $relationshipCandidates = array(); + + foreach($items->execute() as $item) + { + $data = array( + 'id' => intval($item->id), + 'parent' => intval($item->parent_id), + 'owner_id' => intval($item->{'owner_id'}), + 'public' => ($item->view_1)?true:false, + 'type' => $item->type // Grmbl + ); + + if (in_array('uitext', $display)) { + $ui = array( + 'title' => $item->title, + 'description' => $item->description, + 'name' => $item->name, + 'slug' => $item->slug + ); + + $data = array_merge($data, $ui); + } + + + if (in_array('uiimage', $display)) { + $ui = array( + 'height' => $item->height, + 'width' => $item->width, + 'resize_height' => $item->resize_height, + 'resize_width' => $item->resize_width, + 'thumb_height' => $item->resize_height, + 'thumb_width' => $item->resize_width + ); + + $ui['thumb_url_public'] = unrest_rest::size_url('thumbs', $item->relative_path_cache, $item->type); + $public = $item->view_1?true:false; + $fullPublic = $item->view_full_1?true:false; + + if ($item->type != 'album') + { + $ui['file_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=full'); + $ui['thumb_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=thumb'); + $ui['resize_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=resize'); + + if ($public) { + $ui['resize_url_public'] = unrest_rest::size_url('resizes', $item->relative_path_cache, $item->type); + + if ($fullPublic) { + $ui['file_url_public'] = unrest_rest::size_url('albums', $item->relative_path_cache, $item->type); + } + } + } + + $data = array_merge($data, $ui); + } + + if (in_array('members', $display)) { + $filler['children_of'][] = $item->id; + } + + $return[] = array( + 'url' => unrest_rest::makeRestURL('item', $item->id ), + 'entity' => $data + ); + } + + + /* Do we need to fetch children? */ + if (array_key_exists('children_of', $filler)) + { + unrest_rest::addChildren($request, $db, $filler, $permitted, $display, &$return); + } + + return $return; + } +} + + +?> \ No newline at end of file diff --git a/3.0/modules/unrest/module.info b/3.0/modules/unrest/module.info new file mode 100644 index 00000000..aa1c5399 --- /dev/null +++ b/3.0/modules/unrest/module.info @@ -0,0 +1,3 @@ +name = "Un-Rest API module" +description = "Shorter invocations for the Rest API" +version = 1 From f4752e8ecec8323a66084db526ddb485269d25c2 Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Wed, 8 Dec 2010 06:22:48 +0800 Subject: [PATCH 136/300] WebDAV support for G3. --- 3.0/modules/webdav/controllers/webdav.php | 194 ++ 3.0/modules/webdav/helpers/webdav.php | 26 + 3.0/modules/webdav/helpers/webdav_event.php | 23 + .../webdav/libraries/Sabre.autoload.php | 16 + .../webdav/libraries/Sabre.includes.php | 127 ++ .../Sabre/CalDAV/Backend/Abstract.php | 151 ++ .../libraries/Sabre/CalDAV/Backend/PDO.php | 356 ++++ .../libraries/Sabre/CalDAV/Calendar.php | 254 +++ .../libraries/Sabre/CalDAV/CalendarObject.php | 176 ++ .../Sabre/CalDAV/CalendarRootNode.php | 74 + .../Exception/InvalidICalendarObject.php | 18 + .../libraries/Sabre/CalDAV/ICalendarUtil.php | 154 ++ .../webdav/libraries/Sabre/CalDAV/Plugin.php | 707 +++++++ .../SupportedCalendarComponentSet.php | 85 + .../CalDAV/Property/SupportedCalendarData.php | 38 + .../CalDAV/Property/SupportedCollationSet.php | 34 + .../webdav/libraries/Sabre/CalDAV/Server.php | 50 + .../libraries/Sabre/CalDAV/UserCalendars.php | 193 ++ .../webdav/libraries/Sabre/CalDAV/Version.php | 24 + .../webdav/libraries/Sabre/CalDAV/XMLUtil.php | 208 ++ .../Sabre/DAV/Auth/Backend/Abstract.php | 49 + .../Sabre/DAV/Auth/Backend/AbstractBasic.php | 85 + .../Sabre/DAV/Auth/Backend/AbstractDigest.php | 105 + .../Sabre/DAV/Auth/Backend/Apache.php | 77 + .../libraries/Sabre/DAV/Auth/Backend/File.php | 102 + .../libraries/Sabre/DAV/Auth/Backend/PDO.php | 79 + .../libraries/Sabre/DAV/Auth/Plugin.php | 434 ++++ .../libraries/Sabre/DAV/Auth/Principal.php | 147 ++ .../Sabre/DAV/Auth/PrincipalCollection.php | 74 + .../Sabre/DAV/Browser/GuessContentType.php | 97 + .../Sabre/DAV/Browser/MapGetToPropFind.php | 54 + .../libraries/Sabre/DAV/Browser/Plugin.php | 248 +++ .../webdav/libraries/Sabre/DAV/Directory.php | 90 + .../webdav/libraries/Sabre/DAV/Exception.php | 63 + .../Sabre/DAV/Exception/BadRequest.php | 28 + .../Sabre/DAV/Exception/Conflict.php | 28 + .../Sabre/DAV/Exception/ConflictingLock.php | 35 + .../Sabre/DAV/Exception/FileNotFound.php | 28 + .../Sabre/DAV/Exception/Forbidden.php | 27 + .../DAV/Exception/InsufficientStorage.php | 27 + .../DAV/Exception/InvalidResourceType.php | 33 + .../Exception/LockTokenMatchesRequestUri.php | 39 + .../libraries/Sabre/DAV/Exception/Locked.php | 67 + .../Sabre/DAV/Exception/MethodNotAllowed.php | 44 + .../Sabre/DAV/Exception/NotAuthenticated.php | 28 + .../Sabre/DAV/Exception/NotImplemented.php | 27 + .../DAV/Exception/PreconditionFailed.php | 69 + .../DAV/Exception/ReportNotImplemented.php | 30 + .../RequestedRangeNotSatisfiable.php | 29 + .../DAV/Exception/UnsupportedMediaType.php | 28 + .../libraries/Sabre/DAV/FS/Directory.php | 121 ++ .../webdav/libraries/Sabre/DAV/FS/File.php | 89 + .../webdav/libraries/Sabre/DAV/FS/Node.php | 81 + .../libraries/Sabre/DAV/FSExt/Directory.php | 135 ++ .../webdav/libraries/Sabre/DAV/FSExt/File.php | 88 + .../webdav/libraries/Sabre/DAV/FSExt/Node.php | 276 +++ .../webdav/libraries/Sabre/DAV/File.php | 81 + .../libraries/Sabre/DAV/ICollection.php | 58 + .../Sabre/DAV/IExtendedCollection.php | 28 + .../webdav/libraries/Sabre/DAV/IFile.php | 63 + .../webdav/libraries/Sabre/DAV/ILockable.php | 38 + .../webdav/libraries/Sabre/DAV/INode.php | 44 + .../libraries/Sabre/DAV/IProperties.php | 67 + .../webdav/libraries/Sabre/DAV/IQuota.php | 27 + .../Sabre/DAV/Locks/Backend/Abstract.php | 46 + .../libraries/Sabre/DAV/Locks/Backend/FS.php | 180 ++ .../libraries/Sabre/DAV/Locks/Backend/PDO.php | 141 ++ .../libraries/Sabre/DAV/Locks/LockInfo.php | 81 + .../libraries/Sabre/DAV/Locks/Plugin.php | 653 ++++++ .../libraries/Sabre/DAV/Mount/Plugin.php | 79 + .../webdav/libraries/Sabre/DAV/Node.php | 55 + .../webdav/libraries/Sabre/DAV/ObjectTree.php | 149 ++ .../webdav/libraries/Sabre/DAV/Property.php | 25 + .../Sabre/DAV/Property/GetLastModified.php | 75 + .../libraries/Sabre/DAV/Property/Href.php | 91 + .../libraries/Sabre/DAV/Property/IHref.php | 25 + .../Sabre/DAV/Property/LockDiscovery.php | 85 + .../Sabre/DAV/Property/Principal.php | 126 ++ .../Sabre/DAV/Property/ResourceType.php | 80 + .../libraries/Sabre/DAV/Property/Response.php | 156 ++ .../Sabre/DAV/Property/SupportedLock.php | 76 + .../Sabre/DAV/Property/SupportedReportSet.php | 110 + .../webdav/libraries/Sabre/DAV/Server.php | 1821 +++++++++++++++++ .../libraries/Sabre/DAV/ServerPlugin.php | 60 + .../libraries/Sabre/DAV/SimpleDirectory.php | 106 + .../Sabre/DAV/TemporaryFileFilterPlugin.php | 275 +++ .../webdav/libraries/Sabre/DAV/Tree.php | 192 ++ .../libraries/Sabre/DAV/Tree/Filesystem.php | 124 ++ .../webdav/libraries/Sabre/DAV/URLUtil.php | 141 ++ .../webdav/libraries/Sabre/DAV/Version.php | 24 + .../webdav/libraries/Sabre/DAV/XMLUtil.php | 160 ++ .../webdav/libraries/Sabre/HTTP/AWSAuth.php | 226 ++ .../libraries/Sabre/HTTP/AbstractAuth.php | 111 + .../webdav/libraries/Sabre/HTTP/BasicAuth.php | 61 + .../libraries/Sabre/HTTP/DigestAuth.php | 234 +++ .../webdav/libraries/Sabre/HTTP/Request.php | 220 ++ .../webdav/libraries/Sabre/HTTP/Response.php | 151 ++ .../webdav/libraries/Sabre/HTTP/Util.php | 65 + .../webdav/libraries/Sabre/HTTP/Version.php | 24 + .../webdav/libraries/Sabre/autoload.php | 27 + 3.0/modules/webdav/models/author_record.php | 21 + 3.0/modules/webdav/module.info | 3 + .../webdav/views/author_block.html.php | 6 + 103 files changed, 12660 insertions(+) create mode 100644 3.0/modules/webdav/controllers/webdav.php create mode 100644 3.0/modules/webdav/helpers/webdav.php create mode 100644 3.0/modules/webdav/helpers/webdav_event.php create mode 100755 3.0/modules/webdav/libraries/Sabre.autoload.php create mode 100755 3.0/modules/webdav/libraries/Sabre.includes.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/Abstract.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/ICalendarUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php create mode 100755 3.0/modules/webdav/libraries/Sabre/CalDAV/XMLUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractBasic.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Directory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/BadRequest.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Conflict.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/ConflictingLock.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Forbidden.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/InsufficientStorage.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/InvalidResourceType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotImplemented.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/PreconditionFailed.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FS/Directory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/File.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ICollection.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IExtendedCollection.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IFile.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ILockable.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/INode.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IProperties.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/Abstract.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/FS.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Node.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ObjectTree.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/GetLastModified.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/LockDiscovery.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Server.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/SimpleDirectory.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Tree.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/Version.php create mode 100755 3.0/modules/webdav/libraries/Sabre/DAV/XMLUtil.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Request.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Response.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Util.php create mode 100755 3.0/modules/webdav/libraries/Sabre/HTTP/Version.php create mode 100755 3.0/modules/webdav/libraries/Sabre/autoload.php create mode 100644 3.0/modules/webdav/models/author_record.php create mode 100644 3.0/modules/webdav/module.info create mode 100644 3.0/modules/webdav/views/author_block.html.php diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php new file mode 100644 index 00000000..6d2c443a --- /dev/null +++ b/3.0/modules/webdav/controllers/webdav.php @@ -0,0 +1,194 @@ +setBaseUri(url::site('webdav/gallery')); + $server->addPlugin($lock); + $server->addPlugin($filter); + + $this->doAuthenticate(); + $server->exec(); + } + + private function doAuthenticate() { + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setRealm('Gallery3'); + $authResult = $auth->getUserPass(); + list($username, $password) = $authResult; + + if ($username == '' || $password == '') { + $auth->requireLogin(); + die; + } + + $user = identity::lookup_user_by_name($username); + if (empty($user) || !identity::is_correct_password($user, $password)) { + $auth->requireLogin(); + die; + } + + identity::set_active_user($user); + return $user; + } +} + + +class Gallery3Album extends Sabre_DAV_Directory { + private $item; + private $stat; + + function __construct($name) { + $this->item = ORM::factory("item") + ->where("relative_path_cache", "=", rawurlencode($name)) + ->find(); + } + + function getName() { + return $this->item->name; + } + + function getChild($name) { + $rp = $this->item->relative_path() . '/' . rawurlencode($name); + if (substr($rp,0,1) == '/') { $rp = substr($rp, 1); } + + $child = ORM::factory("item") + ->where("relative_path_cache", "=", $rp) + ->find(); + + if (! access::can('view', $child)) { + return false; + }; + + if ($child->type == 'album') { + return new Gallery3Album($this->item->relative_path() . $child->name); + } else { + return new Gallery3File($rp); + } + } + + public function createFile($name, $data = null) { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (substr($name, 0, 1) == '.') { return true; }; + + $tempfile = tempnam(TMPPATH, 'dav'); + $target = fopen($tempfile, 'wb'); + stream_copy_to_stream($data, $target); + fclose($target); + + $parent_id = $this->item->__get('id'); + $item = ORM::factory("item"); + $item->name = $name; + $item->title = item::convert_filename_to_title($item->name); + $item->description = ''; + $item->parent_id = $parent_id; + $item->set_data_file($tempfile); + $item->type = "photo"; + $item->save(); + } + + public function createDirectory($name) { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + + $parent_id = $this->item->__get('id'); + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $parent_id; + $album->name = $name; + $album->title = $name; + $album->description = ''; + $album->save(); + + $this->item = ORM::factory("item")->where('id', '=', $parent_id); + } + + function setName($name) { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + + $this->item->name = $name; + $this->item->save(); + } + + public function delete() { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + $this->item->delete(); + } + + function getChildren() { + $return = array(); + foreach ($this->item->children() as $child) { + $item = $this->getChild($child->name); + if ($item != false) { + $return[] = $item; + } + } + return $return; + } +} + +class Gallery3File extends Sabre_DAV_File { + private $item; + private $stat; + private $path; + + function __construct($path) { + $this->item = ORM::factory("item") + ->where("relative_path_cache", "=", $path) + ->find(); + + if (access::can('view_full', $this->item)) { + $this->stat = stat($this->item->file_path()); + $this->path = $this->item->file_path(); + } else { + $this->stat = stat($this->item->resize_path()); + $this->path = $this->item->resize_path(); + } + } + + public function delete() { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + $this->item->delete(); + } + + function setName($name) { + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + $this->item->name = $name; + $this->item->save(); + } + + public function getLastModified() { + return $this->stat[9]; + } + + function get() { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + return fopen($this->path,'r'); + } + + function getSize() { + return $this->stat[7]; + } + + function getName() { + return $this->item->name; + } + + function getETag() { + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + return '"' . md5($this->item->file_path()) . '"'; + } +} + +?> \ No newline at end of file diff --git a/3.0/modules/webdav/helpers/webdav.php b/3.0/modules/webdav/helpers/webdav.php new file mode 100644 index 00000000..fa7c3fb2 --- /dev/null +++ b/3.0/modules/webdav/helpers/webdav.php @@ -0,0 +1,26 @@ + array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $properties + * @return bool|array + */ + public function updateCalendar($calendarId, array $properties) { + + return false; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + abstract function deleteCalendar($calendarId); + + /** + * Returns all calendar objects within a calendar object. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calnedar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * + * @param string $calendarId + * @return array + */ + abstract function getCalendarObjects($calendarId); + + /** + * Returns information from a single calendar object, based on it's object uri. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + abstract function getCalendarObject($calendarId,$objectUri); + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + abstract function createCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + abstract function updateCalendarObject($calendarId,$objectUri,$calendarData); + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + abstract function deleteCalendarObject($calendarId,$objectUri); + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php new file mode 100755 index 00000000..f60d1737 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php @@ -0,0 +1,356 @@ + 'displayname', + '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description', + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone', + '{http://apple.com/ns/ical/}calendar-order' => 'calendarorder', + '{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor', + ); + + /** + * Creates the backend + * + * @param PDO $pdo + */ + public function __construct(PDO $pdo) { + + $this->pdo = $pdo; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri, which the basename of the uri with which the calendar is + * accessed. + * * principalUri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * @param string $principalUri + * @return array + */ + public function getCalendarsForUser($principalUri) { + + $fields = array_values($this->propertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'ctag'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + + // Making fields a comma-delimited list + $fields = implode(', ', $fields); + $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM calendars WHERE principaluri = ?"); + $stmt->execute(array($principalUri)); + + $calendars = array(); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + + $components = explode(',',$row['components']); + + $calendar = array( + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['principaluri'], + '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0', + '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components), + ); + + + foreach($this->propertyMap as $xmlName=>$dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + $calendars[] = $calendar; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used to reference + * this calendar in other methods, such as updateCalendar + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return mixed + */ + public function createCalendar($principalUri,$calendarUri, array $properties) { + + $fieldNames = array( + 'principaluri', + 'uri', + 'ctag', + ); + $values = array( + ':principaluri' => $principalUri, + ':uri' => $calendarUri, + ':ctag' => 1, + ); + + // Default value + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; + $fieldNames[] = 'components'; + if (!isset($properties[$sccs])) { + $values[':components'] = 'VEVENT,VTODO'; + } else { + if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) { + throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet'); + } + $values[':components'] = implode(',',$properties[$sccs]->getValue()); + } + + foreach($this->propertyMap as $xmlName=>$dbName) { + if (isset($properties[$xmlName])) { + + $myValue = $properties[$xmlName]; + $values[':' . $dbName] = $properties[$xmlName]; + $fieldNames[] = $dbName; + } + } + + $stmt = $this->pdo->prepare("INSERT INTO calendars (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")"); + $stmt->execute($values); + + return $this->pdo->lastInsertId(); + + } + + /** + * Updates a calendars properties + * + * The properties array uses the propertyName in clark-notation as key, + * and the array value for the property value. In the case a property + * should be deleted, the property value will be null. + * + * This method must be atomic. If one property cannot be changed, the + * entire operation must fail. + * + * If the operation was successful, true can be returned. + * If the operation failed, false can be returned. + * + * Deletion of a non-existant property is always succesful. + * + * Lastly, it is optional to return detailed information about any + * failures. In this case an array should be returned with the following + * structure: + * + * array( + * 403 => array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param string $calendarId + * @param array $properties + * @return bool|array + */ + public function updateCalendar($calendarId, array $properties) { + + $newValues = array(); + $result = array( + 200 => array(), // Ok + 403 => array(), // Forbidden + 424 => array(), // Failed Dependency + ); + + $hasError = false; + + foreach($properties as $propertyName=>$propertyValue) { + + // We don't know about this property. + if (!isset($this->propertyMap[$propertyName])) { + $hasError = true; + $result[403][$propertyName] = null; + unset($properties[$propertyName]); + continue; + } + + $fieldName = $this->propertyMap[$propertyName]; + $newValues[$fieldName] = $propertyValue; + + } + + // If there were any errors we need to fail the request + if ($hasError) { + // Properties has the remaining properties + foreach($properties as $propertyName=>$propertyValue) { + $result[424][$propertyName] = null; + } + + // Removing unused statuscodes for cleanliness + foreach($result as $status=>$properties) { + if (is_array($properties) && count($properties)===0) unset($result[$status]); + } + + return $result; + + } + + // Success + + // Now we're generating the sql query. + $valuesSql = array(); + foreach($newValues as $fieldName=>$value) { + $valuesSql[] = $fieldName . ' = ?'; + } + $valuesSql[] = 'ctag = ctag + 1'; + + $stmt = $this->pdo->prepare("UPDATE calendars SET " . implode(', ',$valuesSql) . " WHERE id = ?"); + $newValues['id'] = $calendarId; + $stmt->execute(array_values($newValues)); + + return true; + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + public function deleteCalendar($calendarId) { + + $stmt = $this->pdo->prepare('DELETE FROM calendarobjects WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + + $stmt = $this->pdo->prepare('DELETE FROM calendars WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Returns all calendar objects within a calendar object. + * + * Every item contains an array with the following keys: + * * id - unique identifier which will be used for subsequent updates + * * calendardata - The iCalendar-compatible calnedar data + * * uri - a unique key which will be used to construct the uri. This can be any arbitrary string. + * * lastmodified - a timestamp of the last modification time + * + * @param string $calendarId + * @return array + */ + public function getCalendarObjects($calendarId) { + + $stmt = $this->pdo->prepare('SELECT * FROM calendarobjects WHERE calendarid = ?'); + $stmt->execute(array($calendarId)); + return $stmt->fetchAll(); + + } + + /** + * Returns information from a single calendar object, based on it's object uri. + * + * @param string $calendarId + * @param string $objectUri + * @return array + */ + public function getCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('SELECT * FROM calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId, $objectUri)); + return $stmt->fetch(); + + } + + /** + * Creates a new calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function createCalendarObject($calendarId,$objectUri,$calendarData) { + + $stmt = $this->pdo->prepare('INSERT INTO calendarobjects (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)'); + $stmt->execute(array($calendarId,$objectUri,$calendarData,time())); + $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * @param string $calendarId + * @param string $objectUri + * @param string $calendarData + * @return void + */ + public function updateCalendarObject($calendarId,$objectUri,$calendarData) { + + $stmt = $this->pdo->prepare('UPDATE calendarobjects SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarData,time(),$calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + /** + * Deletes an existing calendar object. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + public function deleteCalendarObject($calendarId,$objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute(array($calendarId,$objectUri)); + $stmt = $this->pdo->prepare('UPDATE calendars SET ctag = ctag + 1 WHERE id = ?'); + $stmt->execute(array($calendarId)); + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php new file mode 100755 index 00000000..b99878b7 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php @@ -0,0 +1,254 @@ +caldavBackend = $caldavBackend; + $this->authBackend = $authBackend; + $this->calendarInfo = $calendarInfo; + + + } + + /** + * Returns the name of the calendar + * + * @return string + */ + public function getName() { + + return $this->calendarInfo['uri']; + + } + + /** + * Updates properties such as the display name and description + * + * @param array $mutations + * @return array + */ + public function updateProperties($mutations) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations); + + } + + /** + * Returns the list of properties + * + * @param array $properties + * @return array + */ + public function getProperties($requestedProperties) { + + $response = array(); + + if (!$this->hasPrivilege()) return array(); + + foreach($requestedProperties as $prop) switch($prop) { + + case '{DAV:}resourcetype' : + $response[$prop] = new Sabre_DAV_Property_ResourceType(array('{urn:ietf:params:xml:ns:caldav}calendar','{DAV:}collection')); + break; + case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); + break; + case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : + $response[$prop] = new Sabre_CalDAV_Property_SupportedCollationSet(); + break; + case '{DAV:}owner' : + $response[$prop] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::HREF,$this->calendarInfo['principaluri']); + break; + default : + if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop]; + break; + + } + return $response; + + } + + /** + * Returns a calendar object + * + * The contained calendar objects are for example Events or Todo's. + * + * @param string $name + * @return Sabre_DAV_ICalendarObject + */ + public function getChild($name) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Calendar object not found'); + return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + + } + + /** + * Returns the full list of calendar objects + * + * @return array + */ + public function getChildren() { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); + $children = array(); + foreach($objs as $obj) { + $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj); + } + return $children; + + } + + /** + * Checks if a child-node exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name); + if (!$obj) + return false; + else + return true; + + } + + /** + * Creates a new directory + * + * We actually block this, as subdirectories are not allowed in calendars. + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed'); + + } + + /** + * Creates a new file + * + * The contents of the new file must be a valid ICalendar string. + * + * @param string $name + * @param resource $calendarData + * @return void + */ + public function createFile($name,$calendarData = null) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $calendarData = stream_get_contents($calendarData); + + $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue(); + Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); + + $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData); + + } + + /** + * Deletes the calendar. + * + * @return void + */ + public function delete() { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + $this->caldavBackend->deleteCalendar($this->calendarInfo['id']); + + } + + /** + * Renames the calendar. Note that most calendars use the + * {DAV:}displayname to display a name to display a name. + * + * @param string $newName + * @return void + */ + public function setName($newName) { + + if (!$this->hasPrivilege()) throw new Sabre_DAV_Exception_Forbidden('Permission denied to access this calendar'); + throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported'); + + } + + /** + * Returns the last modification date as a unix timestamp. + * + * @return void + */ + public function getLastModified() { + + return null; + + } + + /** + * Check if user has access. + * + * This method does a check if the currently logged in user + * has permission to access this calendar. There is only read-write + * access, so you're in or you're out. + * + * @return bool + */ + protected function hasPrivilege() { + + if (!$user = $this->authBackend->getCurrentUser()) return false; + if ($user['uri']!==$this->calendarInfo['principaluri']) return false; + return true; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php new file mode 100755 index 00000000..937bab19 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php @@ -0,0 +1,176 @@ +caldavBackend = $caldavBackend; + $this->calendarInfo = $calendarInfo; + $this->objectData = $objectData; + + } + + /** + * Returns the uri for this object + * + * @return string + */ + public function getName() { + + return $this->objectData['uri']; + + } + + /** + * Returns the ICalendar-formatted object + * + * @return string + */ + public function get() { + + return $this->objectData['calendardata']; + + } + + /** + * Updates the ICalendar-formatted object + * + * @param string $calendarData + * @return void + */ + public function put($calendarData) { + + if (is_resource($calendarData)) + $calendarData = stream_get_contents($calendarData); + + $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set']->getValue(); + Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents); + + $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData); + $this->objectData['calendardata'] = $calendarData; + + } + + /** + * Deletes the calendar object + * + * @return void + */ + public function delete() { + + $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']); + + } + + /** + * Returns the mime content-type + * + * @return string + */ + public function getContentType() { + + return 'text/calendar'; + + } + + /** + * Returns an ETag for this object. + * + * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * + * @return string + */ + public function getETag() { + + return '"' . md5($this->objectData['calendardata']). '"'; + + } + + /** + * Returns the list of properties for this object + * + * @param array $properties + * @return array + */ + public function getProperties($properties) { + + $response = array(); + if (in_array('{urn:ietf:params:xml:ns:caldav}calendar-data',$properties)) + $response['{urn:ietf:params:xml:ns:caldav}calendar-data'] = str_replace("\r","",$this->objectData['calendardata']); + + + return $response; + + } + + /** + * Updates properties + * + * @param array $properties + * @return array + */ + public function updateProperties($properties) { + + return false; + + } + + /** + * Returns the last modification date as a unix timestamp + * + * @return time + */ + public function getLastModified() { + + return strtotime($this->objectData['lastmodified']); + + } + + /** + * Returns the size of this object in bytes + * + * @return int + */ + public function getSize() { + + return strlen($this->objectData['calendardata']); + + } +} + diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php new file mode 100755 index 00000000..322722a1 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php @@ -0,0 +1,74 @@ +authBackend = $authBackend; + $this->caldavBackend = $caldavBackend; + + } + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + + return Sabre_CalDAV_Plugin::CALENDAR_ROOT; + + } + + /** + * Returns the list of users as Sabre_CalDAV_User objects. + * + * @return array + */ + public function getChildren() { + + $users = $this->authBackend->getUsers(); + $children = array(); + foreach($users as $user) { + + $children[] = new Sabre_CalDAV_UserCalendars($this->authBackend, $this->caldavBackend, $user['uri']); + + } + return $children; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php new file mode 100755 index 00000000..24873f5d --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php @@ -0,0 +1,18 @@ +registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal'); + + // Check if there's only 1 component + $components = array('vevent','vtodo','vjournal','vfreebusy'); + $componentsFound = array(); + + foreach($components as $component) { + $test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component); + if (is_array($test)) $componentsFound = array_merge($componentsFound, $test); + } + if (count($componentsFound)>1) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Only 1 of VEVENT, VTODO, VJOURNAL or VFREEBUSY may be specified per calendar object'); + } + if (count($componentsFound)<1) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.'); + } + $component = $componentsFound[0]; + + // Check if the component is allowed + $name = $component->getName(); + if (!in_array(strtoupper($name),$allowedComponents)) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.'); + } + + if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) { + throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects'); + } + + return true; + + } + + /** + * Converts ICalendar data to XML. + * + * Properties are converted to lowercase xml elements. Parameters are; + * converted to attributes. BEGIN:VEVENT is converted to and + * END:VEVENT as well as other components. + * + * It's a very loose parser. If any line does not conform to the spec, it + * will simply be ignored. It will try to detect if \r\n or \n line endings + * are used. + * + * @todo Currently quoted attributes are not parsed correctly. + * @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03 + * @param string $icalData + * @return string. + */ + static function toXCAL($icalData) { + + // Detecting line endings + $lb="\r\n"; + if (strpos($icalData,"\r\n")!==false) $lb = "\r\n"; + elseif (strpos($icalData,"\n")!==false) $lb = "\n"; + + // Splitting up items per line + $lines = explode($lb,$icalData); + + // Properties can be folded over 2 lines. In this case the second + // line will be preceeded by a space or tab. + $lines2 = array(); + foreach($lines as $line) { + + if (!$line) continue; + if ($line[0]===" " || $line[0]==="\t") { + $lines2[count($lines2)-1].=substr($line,1); + continue; + } + + $lines2[]=$line; + + } + + $xml = '' . "\n"; + $xml.= "\n"; + + $spaces = 2; + foreach($lines2 as $line) { + + $matches = array(); + // This matches PROPERTYNAME;ATTRIBUTES:VALUE + if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches)) + continue; + + $propertyName = strtolower($matches[1]); + $attributes = $matches[2]; + $value = $matches[3]; + + // If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it. + if ($propertyName === 'begin') { + $xml.=str_repeat(" ",$spaces); + $xml.='<' . strtolower($value) . ">\n"; + $spaces+=2; + continue; + } elseif ($propertyName === 'end') { + $spaces-=2; + $xml.=str_repeat(" ",$spaces); + $xml.='\n"; + continue; + } + + $xml.=str_repeat(" ",$spaces); + $xml.='<' . $propertyName; + if ($attributes) { + // There can be multiple attributes + $attributes = explode(';',$attributes); + foreach($attributes as $att) { + + list($attName,$attValue) = explode('=',$att,2); + $attName = strtolower($attName); + if ($attName === 'language') $attName='xml:lang'; + $xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"'; + + } + } + + $xml.='>'. htmlspecialchars(trim($value)) . '\n"; + + } + $xml.=""; + return $xml; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php new file mode 100755 index 00000000..7a29891b --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php @@ -0,0 +1,707 @@ +server->tree->getNodeForPath($parent); + + if ($node instanceof Sabre_DAV_IExtendedCollection) { + try { + $node->getChild($name); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + return array('MKCALENDAR'); + } + } + return array(); + + } + + /** + * Returns a list of features for the DAV: HTTP header. + * + * @return array + */ + public function getFeatures() { + + return array('calendar-access'); + + } + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000); + $server->subscribeEvent('report',array($this,'report')); + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + + $server->xmlNamespaces[self::NS_CALDAV] = 'cal'; + $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs'; + + $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet'; + + array_push($server->protectedProperties, + + '{' . self::NS_CALDAV . '}supported-calendar-component-set', + '{' . self::NS_CALDAV . '}supported-calendar-data', + '{' . self::NS_CALDAV . '}max-resource-size', + '{' . self::NS_CALDAV . '}min-date-time', + '{' . self::NS_CALDAV . '}max-date-time', + '{' . self::NS_CALDAV . '}max-instances', + '{' . self::NS_CALDAV . '}max-attendees-per-instance', + '{' . self::NS_CALDAV . '}calendar-home-set', + '{' . self::NS_CALDAV . '}supported-collation-set', + + // scheduling extension + '{' . self::NS_CALDAV . '}calendar-user-address-set' + + ); + } + + /** + * This function handles support for the MKCALENDAR method + * + * @param string $method + * @return bool + */ + public function unknownMethod($method, $uri) { + + if ($method!=='MKCALENDAR') return; + + $this->httpMkCalendar($uri); + // false is returned to stop the unknownMethod event + return false; + + } + + /** + * This functions handles REPORT requests specific to CalDAV + * + * @param string $reportName + * @param DOMNode $dom + * @return bool + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{'.self::NS_CALDAV.'}calendar-multiget' : + $this->calendarMultiGetReport($dom); + return false; + case '{'.self::NS_CALDAV.'}calendar-query' : + $this->calendarQueryReport($dom); + return false; + default : + return true; + + } + + + } + + /** + * This function handles the MKCALENDAR HTTP method, which creates + * a new calendar. + * + * @param string $uri + * @return void + */ + public function httpMkCalendar($uri) { + + // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support + // for clients matching iCal in the user agent + //$ua = $this->server->httpRequest->getHeader('User-Agent'); + //if (strpos($ua,'iCal/')!==false) { + // throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.'); + //} + + $body = $this->server->httpRequest->getBody(true); + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $properties = array(); + foreach($dom->firstChild->childNodes as $child) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue; + foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) { + $properties[$k] = $prop; + } + + } + + $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar'); + + $this->server->createCollection($uri,$resourceType,$properties); + + $this->server->httpResponse->sendStatus(201); + $this->server->httpResponse->setHeader('Content-Length',0); + } + + /** + * afterGetProperties + * + * This method handler is invoked after properties for a specific resource + * are received. This allows us to add any properties that might have been + * missing. + * + * @param string $path + * @param array $properties + * @return void + */ + public function afterGetProperties($path, &$properties) { + + // Find out if we are currently looking at a principal resource + $currentNode = $this->server->tree->getNodeForPath($path); + if ($currentNode instanceof Sabre_DAV_Auth_Principal) { + + // calendar-home-set property + $calHome = '{' . self::NS_CALDAV . '}calendar-home-set'; + if (array_key_exists($calHome,$properties[404])) { + $principalId = $currentNode->getName(); + $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/'; + unset($properties[404][$calHome]); + $properties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath); + } + + // calendar-user-address-set property + $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set'; + if (array_key_exists($calProp,$properties[404])) { + + // Do we have an email address? + $props = $currentNode->getProperties(array('{http://sabredav.org/ns}email-address')); + if (isset($props['{http://sabredav.org/ns}email-address'])) { + $email = $props['{http://sabredav.org/ns}email-address']; + } else { + // We're going to make up an emailaddress + $email = $currentNode->getName() . '.sabredav@' . $this->server->httpRequest->getHeader('host'); + } + $properties[200][$calProp] = new Sabre_DAV_Property_Href('mailto:' . $email, false); + unset($properties[404][$calProp]); + + } + + + } + + if ($currentNode instanceof Sabre_CalDAV_Calendar || $currentNode instanceof Sabre_CalDAV_CalendarObject) { + if (array_key_exists('{DAV:}supported-report-set', $properties[200])) { + $properties[200]['{DAV:}supported-report-set']->addReport(array( + '{' . self::NS_CALDAV . '}calendar-multiget', + '{' . self::NS_CALDAV . '}calendar-query', + // '{' . self::NS_CALDAV . '}supported-collation-set', + // '{' . self::NS_CALDAV . '}free-busy-query', + )); + } + } + + + } + + /** + * This function handles the calendar-multiget REPORT. + * + * This report is used by the client to fetch the content of a series + * of urls. Effectively avoiding a lot of redundant requests. + * + * @param DOMNode $dom + * @return void + */ + public function calendarMultiGetReport($dom) { + + $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + + $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href'); + foreach($hrefElems as $elem) { + $uri = $this->server->calculateUri($elem->nodeValue); + list($objProps) = $this->server->getPropertiesForPath($uri,$properties); + $propertyList[]=$objProps; + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); + + } + + /** + * This function handles the calendar-query REPORT + * + * This report is used by clients to request calendar objects based on + * complex conditions. + * + * @param DOMNode $dom + * @return void + */ + public function calendarQueryReport($dom) { + + $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); + + $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter'); + if ($filterNode->length!==1) { + throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element'); + } + $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0)); + + $requestedCalendarData = true; + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { + // We always retrieve calendar-data, as we need it for filtering. + $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; + + // If calendar-data wasn't explicitly requested, we need to remove + // it after processing. + $requestedCalendarData = false; + } + + // These are the list of nodes that potentially match the requirement + $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(),$requestedProperties,$this->server->getHTTPDepth(0)); + + $verifiedNodes = array(); + + foreach($candidateNodes as $node) { + + // If the node didn't have a calendar-data property, it must not be a calendar object + if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) continue; + + if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'],$filters)) { + + if (!$requestedCalendarData) { + unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); + } + $verifiedNodes[] = $node; + } + + } + + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); + + } + + + /** + * Verify if a list of filters applies to the calendar data object + * + * The calendarData object must be a valid iCalendar blob. The list of + * filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters + * + * @param string $calendarData + * @param array $filters + * @return bool + */ + public function validateFilters($calendarData,$filters) { + + // We are converting the calendar object to an XML structure + // This makes it far easier to parse + $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData); + $xml = simplexml_load_string($xCalendarData); + $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal'); + + foreach($filters as $xpath=>$filter) { + + // if-not-defined comes first + if (isset($filter['is-not-defined'])) { + if (!$xml->xpath($xpath)) + continue; + else + return false; + + } + + $elem = $xml->xpath($xpath); + + if (!$elem) return false; + $elem = $elem[0]; + + if (isset($filter['time-range'])) { + + switch($elem->getName()) { + case 'vevent' : + $result = $this->validateTimeRangeFilterForEvent($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'vtodo' : + $result = $this->validateTimeRangeFilterForTodo($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'vjournal' : + // TODO: not implemented + break; + $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'vfreebusy' : + // TODO: not implemented + break; + $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter); + if ($result===false) return false; + break; + case 'valarm' : + // TODO: not implemented + break; + $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter); + if ($result===false) return false; + break; + + } + + } + + if (isset($filter['text-match'])) { + $currentString = (string)$elem; + + $isMatching = $this->substringMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']); + if ($filter['text-match']['negate-condition'] && $isMatching) return false; + if (!$filter['text-match']['negate-condition'] && !$isMatching) return false; + + } + + } + return true; + + } + + private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) { + + // Grabbing the DTSTART property + $xdtstart = $xml->xpath($currentXPath.'/c:dtstart'); + if (!count($xdtstart)) { + throw new Sabre_DAV_Exception_BadRequest('DTSTART property missing from calendar object'); + } + + // The dtstart can be both a date, or datetime property + if ((string)$xdtstart[0]['value']==='DATE') { + $isDateTime = false; + } else { + $isDateTime = true; + } + + // Determining the timezone + if ($tzid = (string)$xdtstart[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + if ($isDateTime) { + $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtstart[0],$tz); + } else { + $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtstart[0]); + } + + + // Grabbing the DTEND property + $xdtend = $xml->xpath($currentXPath.'/c:dtend'); + $dtend = null; + + if (count($xdtend)) { + // Determining the timezone + if ($tzid = (string)$xdtend[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + + // Since the VALUE parameter of both DTSTART and DTEND must be the same + // we can assume we don't need to check the VALUE paramter of DTEND. + if ($isDateTime) { + $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz); + } else { + $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtend[0],$tz); + } + + } + + if (is_null($dtend)) { + // The DTEND property was not found. We will first see if the event has a duration + // property + + $xduration = $xml->xpath($currentXPath.'/c:duration'); + if (count($xduration)) { + $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]); + + // Making sure that the duration is bigger than 0 seconds. + $tempDT = clone $dtstart; + $tempDT->modify($duration); + if ($tempDT > $dtstart) { + + // use DTEND = DTSTART + DURATION + $dtend = $tempDT; + } else { + // use DTEND = DTSTART + $dtend = $dtstart; + } + + } + } + + if (is_null($dtend)) { + if ($isDateTime) { + // DTEND = DTSTART + $dtend = $dtstart; + } else { + // DTEND = DTSTART + 1 DAY + $dtend = clone $dtstart; + $dtend->modify('+1 day'); + } + } + + if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend) return false; + if (!is_null($currentFilter['time-range']['end']) && $currentFilter['time-range']['end'] <= $dtstart) return false; + return true; + + } + + private function validateTimeRangeFilterForTodo(SimpleXMLElement $xml,$currentXPath,array $filter) { + + // Gathering all relevant elements + + $dtStart = null; + $duration = null; + $due = null; + $completed = null; + $created = null; + + $xdt = $xml->xpath($currentXPath.'/c:dtstart'); + if (count($xdt)) { + // The dtstart can be both a date, or datetime property + if ((string)$xdt[0]['value']==='DATE') { + $isDateTime = false; + } else { + $isDateTime = true; + } + + // Determining the timezone + if ($tzid = (string)$xdt[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + if ($isDateTime) { + $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz); + } else { + $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]); + } + } + + // Only need to grab duration if dtStart is set + if (!is_null($dtStart)) { + + $xduration = $xml->xpath($currentXPath.'/c:duration'); + if (count($xduration)) { + $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]); + } + + } + + if (!is_null($dtStart) && !is_null($duration)) { + + // Comparision from RFC 4791: + // (start <= DTSTART+DURATION) AND ((end > DTSTART) OR (end >= DTSTART+DURATION)) + + $end = clone $dtStart; + $end->modify($duration); + + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $end) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart || $filter['time-range']['end'] >= $end) ) { + return true; + } else { + return false; + } + + } + + // Need to grab the DUE property + $xdt = $xml->xpath($currentXPath.'/c:due'); + if (count($xdt)) { + // The due property can be both a date, or datetime property + if ((string)$xdt[0]['value']==='DATE') { + $isDateTime = false; + } else { + $isDateTime = true; + } + // Determining the timezone + if ($tzid = (string)$xdt[0]['tzid']) { + $tz = new DateTimeZone($tzid); + } else { + $tz = null; + } + if ($isDateTime) { + $due = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz); + } else { + $due = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]); + } + } + + if (!is_null($dtStart) && !is_null($due)) { + + // Comparision from RFC 4791: + // ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE)) + + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due || $filter['time-range']['start'] < $dtstart) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) { + return true; + } else { + return false; + } + + } + + if (!is_null($dtStart)) { + + // Comparision from RFC 4791 + // (start <= DTSTART) AND (end > DTSTART) + if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $dtStart) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart) ) { + return true; + } else { + return false; + } + + } + + if (!is_null($due)) { + + // Comparison from RFC 4791 + // (start < DUE) AND (end >= DUE) + if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $due) ) { + return true; + } else { + return false; + } + + } + // Need to grab the COMPLETED property + $xdt = $xml->xpath($currentXPath.'/c:completed'); + if (count($xdt)) { + $completed = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]); + } + // Need to grab the CREATED property + $xdt = $xml->xpath($currentXPath.'/c:created'); + if (count($xdt)) { + $created = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]); + } + + if (!is_null($completed) && !is_null($created)) { + // Comparison from RFC 4791 + // ((start <= CREATED) OR (start <= COMPLETED)) AND ((end >= CREATED) OR (end >= COMPLETED)) + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $created || $filter['time-range']['start'] <= $completed) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $created || $filter['time-range']['end'] >= $completed)) { + return true; + } else { + return false; + } + } + + if (!is_null($completed)) { + // Comparison from RFC 4791 + // (start <= COMPLETED) AND (end >= COMPLETED) + if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $completed) && + (is_null($filter['time-range']['end']) || $filter['time-range']['end'] >= $completed)) { + return true; + } else { + return false; + } + } + + if (!is_null($created)) { + // Comparison from RFC 4791 + // (end > CREATED) + if( (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $created) ) { + return true; + } else { + return false; + } + } + + // Everything else is TRUE + return true; + + } + + public function substringMatch($haystack, $needle, $collation) { + + switch($collation) { + case 'i;ascii-casemap' : + // default strtolower takes locale into consideration + // we don't want this. + $haystack = str_replace(range('a','z'), range('A','Z'), $haystack); + $needle = str_replace(range('a','z'), range('A','Z'), $needle); + return strpos($haystack, $needle)!==false; + + case 'i;octet' : + return strpos($haystack, $needle)!==false; + + default: + throw new Sabre_DAV_Exception_BadRequest('Unknown collation: ' . $collation); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php new file mode 100755 index 00000000..5a1862d0 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php @@ -0,0 +1,85 @@ +components = $components; + + } + + /** + * Returns the list of supported components + * + * @return array + */ + public function getValue() { + + return $this->components; + + } + + /** + * Serializes the property in a DOMDocument + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $node) { + + $doc = $node->ownerDocument; + foreach($this->components as $component) { + + $xcomp = $doc->createElement('cal:comp'); + $xcomp->setAttribute('name',$component); + $node->appendChild($xcomp); + + } + + } + + /** + * Unserializes the DOMElement back into a Property class. + * + * @param DOMElement $node + * @return void + */ + static function unserialize(DOMElement $node) { + + $components = array(); + foreach($node->childNodes as $childNode) { + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') { + $components[] = $childNode->getAttribute('name'); + } + } + return new self($components); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php new file mode 100755 index 00000000..3e9a9d76 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php @@ -0,0 +1,38 @@ +ownerDocument; + + $prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal'; + + $caldata = $doc->createElement($prefix . ':calendar-data'); + $caldata->setAttribute('content-type','text/calendar'); + $caldata->setAttribute('version','2.0'); + + $node->appendChild($caldata); + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php new file mode 100755 index 00000000..2f96591b --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php @@ -0,0 +1,34 @@ +ownerDocument; + + $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav'); + if (!$prefix) $prefix = 'cal'; + + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;ascii-casemap') + ); + $node->appendChild( + $doc->createElement($prefix . ':supported-collation','i;octet') + ); + + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php new file mode 100755 index 00000000..33f7a39c --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php @@ -0,0 +1,50 @@ +addChild($principals); + $calendars = new Sabre_CalDAV_CalendarRootNode($authBackend, $calendarBackend); + $root->addChild($calendars); + + $objectTree = new Sabre_DAV_ObjectTree($root); + + /* Initializing server */ + parent::__construct($objectTree); + + /* Server Plugins */ + $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,'SabreDAV'); + $this->addPlugin($authPlugin); + + $caldavPlugin = new Sabre_CalDAV_Plugin(); + $this->addPlugin($caldavPlugin); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php new file mode 100755 index 00000000..c81b70ba --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php @@ -0,0 +1,193 @@ +authBackend = $authBackend; + $this->caldavBackend = $caldavBackend; + $this->userUri = $userUri; + + } + + /** + * Returns the name of this object + * + * @return string + */ + public function getName() { + + list(,$name) = Sabre_DAV_URLUtil::splitPath($this->userUri); + return $name; + + } + + /** + * Updates the name of this object + * + * @param string $name + * @return void + */ + public function setName($name) { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Deletes this object + * + * @return void + */ + public function delete() { + + throw new Sabre_DAV_Exception_Forbidden(); + + } + + /** + * Returns the last modification date + * + * @return int + */ + public function getLastModified() { + + return null; + + } + + /** + * Creates a new file under this object. + * + * This is currently not allowed + * + * @param string $filename + * @param resource $data + * @return void + */ + public function createFile($filename, $data=null) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported'); + + } + + /** + * Creates a new directory under this object. + * + * This is currently not allowed. + * + * @param string $filename + * @return void + */ + public function createDirectory($filename) { + + throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported'); + + } + + /** + * Returns a single calendar, by name + * + * @param string $name + * @todo needs optimizing + * @return Sabre_CalDAV_Calendar + */ + public function getChild($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return $child; + + } + throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found'); + + } + + /** + * Checks if a calendar exists. + * + * @param string $name + * @todo needs optimizing + * @return bool + */ + public function childExists($name) { + + foreach($this->getChildren() as $child) { + if ($name==$child->getName()) + return true; + + } + return false; + + } + + /** + * Returns a list of calendars + * + * @return array + */ + public function getChildren() { + + $calendars = $this->caldavBackend->getCalendarsForUser($this->userUri); + $objs = array(); + foreach($calendars as $calendar) { + $objs[] = new Sabre_CalDAV_Calendar($this->authBackend, $this->caldavBackend, $calendar); + } + return $objs; + + } + + /** + * Creates a new calendar + * + * @param string $name + * @param string $properties + * @return void + */ + public function createExtendedCollection($name, array $resourceType, array $properties) { + + if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) { + throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection'); + } + $this->caldavBackend->createCalendar($this->userUri, $name, $properties); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php b/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php new file mode 100755 index 00000000..d7b2e6af --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php @@ -0,0 +1,24 @@ +childNodes as $child) { + + switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) { + + case '{urn:ietf:params:xml:ns:caldav}comp-filter' : + case '{urn:ietf:params:xml:ns:caldav}prop-filter' : + + $filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name')); + $filters[$filterName] = array(); + + self::parseCalendarQueryFilters($child, $filterName,$filters); + break; + + case '{urn:ietf:params:xml:ns:caldav}time-range' : + + if ($start = $child->getAttribute('start')) { + $start = self::parseICalendarDateTime($start); + } else { + $start = null; + } + if ($end = $child->getAttribute('end')) { + $end = self::parseICalendarDateTime($end); + } else { + $end = null; + } + + if (!is_null($start) && !is_null($end) && $end <= $start) { + throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter'); + } + + $filters[$basePath]['time-range'] = array( + 'start' => $start, + 'end' => $end + ); + break; + + case '{urn:ietf:params:xml:ns:caldav}is-not-defined' : + $filters[$basePath]['is-not-defined'] = true; + break; + + case '{urn:ietf:params:xml:ns:caldav}param-filter' : + + $filterName = $basePath . '/@' . strtolower($child->getAttribute('name')); + $filters[$filterName] = array(); + self::parseCalendarQueryFilters($child, $filterName, $filters); + break; + + case '{urn:ietf:params:xml:ns:caldav}text-match' : + + $collation = $child->getAttribute('collation'); + if (!$collation) $collation = 'i;ascii-casemap'; + + $filters[$basePath]['text-match'] = array( + 'collation' => $collation, + 'negate-condition' => $child->getAttribute('negate-condition')==='yes', + 'value' => $child->nodeValue, + ); + break; + + } + + } + + return $filters; + + } + + /** + * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object + * + * Specifying a reference timezone is optional. It will only be used + * if the non-UTC format is used. The argument is used as a reference, the + * returned DateTime object will still be in the UTC timezone. + * + * @param string $dt + * @param DateTimeZone $tz + * @return DateTime + */ + static public function parseICalendarDateTime($dt,DateTimeZone $tz = null) { + + // Format is YYYYMMDD + "T" + hhmmss + $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches); + + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt); + } + + if ($matches[7]==='Z' || is_null($tz)) { + $tz = new DateTimeZone('UTC'); + } + $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz); + + // Still resetting the timezone, to normalize everything to UTC + $date->setTimeZone(new DateTimeZone('UTC')); + return $date; + + } + + /** + * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object + * + * @param string $date + * @param DateTimeZone $tz + * @return DateTime + */ + static public function parseICalendarDate($date) { + + // Format is YYYYMMDD + $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches); + + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date); + } + + $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC')); + return $date; + + } + + /** + * Parses an iCalendar (RFC5545) formatted duration and returns a string suitable + * for strtotime or DateTime::modify. + * + * NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which + * supports ISO 8601 Intervals, which is a superset of ICalendar durations. + * + * For now though, we're just gonna live with this messy system + * + * @param string $duration + * @return string + */ + static public function parseICalendarDuration($duration) { + + $result = preg_match('/^(?P\+|-)?P((?P\d+)W)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?$/', $duration, $matches); + if (!$result) { + throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration); + } + + $parts = array( + 'week', + 'day', + 'hour', + 'minute', + 'second', + ); + + $newDur = ''; + foreach($parts as $part) { + if (isset($matches[$part]) && $matches[$part]) { + $newDur.=' '.$matches[$part] . ' ' . $part . 's'; + } + } + + $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur); + return $newDur; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php new file mode 100755 index 00000000..79383fa3 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php @@ -0,0 +1,49 @@ +currentUser; + } + + + /** + * Authenticates the user based on the current request. + * + * If authentication is succesful, true must be returned. + * If authentication fails, an exception must be thrown. + * + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function authenticate(Sabre_DAV_Server $server,$realm) { + + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setHTTPRequest($server->httpRequest); + $auth->setHTTPResponse($server->httpResponse); + $auth->setRealm($realm); + $userpass = $auth->getUserPass(); + if (!$userpass) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found'); + } + + // Authenticates the user + if (!($userData = $this->validateUserPass($userpass[0],$userpass[1]))) { + $auth->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match'); + } + if (!isset($userData['uri'])) { + throw new Sabre_DAV_Exception('The returned array from validateUserPass must contain at a uri element'); + } + $this->currentUser = $userData; + return true; + } + + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php new file mode 100755 index 00000000..d8cdcb18 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php @@ -0,0 +1,105 @@ +setHTTPRequest($server->httpRequest); + $digest->setHTTPResponse($server->httpResponse); + + $digest->setRealm($realm); + $digest->init(); + + $username = $digest->getUsername(); + + // No username was given + if (!$username) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found'); + } + + $userData = $this->getUserInfo($realm, $username); + // If this was false, the user account didn't exist + if ($userData===false) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file'); + } + if (!is_array($userData)) { + throw new Sabre_DAV_Exception('The returntype for getUserInfo must be either false or an array'); + } + + if (!isset($userData['uri']) || !isset($userData['digestHash'])) { + throw new Sabre_DAV_Exception('The returned array from getUserInfo must contain at least a uri and digestHash element'); + } + + // If this was false, the password or part of the hash was incorrect. + if (!$digest->validateA1($userData['digestHash'])) { + $digest->requireLogin(); + throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username'); + } + + $this->currentUser = $userData; + return true; + + } + + /** + * Returns information about the currently logged in user. + * + * @return array|null + */ + public function getCurrentUser() { + + return $this->currentUser; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php new file mode 100755 index 00000000..0b46270f --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php @@ -0,0 +1,77 @@ +httpRequest->getRawServerValue('REMOTE_USER'); + if (is_null($remoteUser)) { + throw new Sabre_DAV_Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured'); + } + + $this->remoteUser = $remoteUser; + return true; + + } + + /** + * Returns information about the currently logged in user. + * + * If nobody is currently logged in, this method should return null. + * + * @return array|null + */ + public function getCurrentUser() { + + return array( + 'uri' => 'principals/' . $this->remoteUser, + ); + + } + + /** + * Returns the full list of users. + * + * This method must at least return a uri for each user. + * + * It is optional to implement this. + * + * @return array + */ + public function getUsers() { + + return array($this->getCurrentUser()); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php new file mode 100755 index 00000000..953c49e6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php @@ -0,0 +1,102 @@ +loadFile($filename); + + } + + /** + * Loads an htdigest-formatted file. This method can be called multiple times if + * more than 1 file is used. + * + * @param string $filename + * @return void + */ + public function loadFile($filename) { + + foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) { + + if (substr_count($line, ":") !== 2) + throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons'); + + list($username,$realm,$A1) = explode(':',$line); + + if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1)) + throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash'); + + $this->users[$username] = array( + 'digestHash' => $A1, + 'uri' => 'principals/' . $username + ); + + } + + } + + /** + * Returns a users' information + * + * @param string $realm + * @param string $username + * @return string + */ + public function getUserInfo($realm, $username) { + + return isset($this->users[$username])?$this->users[$username]:false; + + } + + + /** + * Returns the full list of users. + * + * This method must at least return a uri for each user. + * + * @return array + */ + public function getUsers() { + + $re = array(); + foreach($this->users as $userName=>$A1) { + + $re[] = array( + 'uri'=>'principals/' . $userName + ); + + } + + return $re; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php new file mode 100755 index 00000000..c8808580 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php @@ -0,0 +1,79 @@ +pdo = $pdo; + + } + + /** + * Returns a users' information + * + * @param string $realm + * @param string $username + * @return string + */ + public function getUserInfo($realm,$username) { + + $stmt = $this->pdo->prepare('SELECT username, digesta1, email FROM users WHERE username = ?'); + $stmt->execute(array($username)); + $result = $stmt->fetchAll(); + + if (!count($result)) return false; + $user = array( + 'uri' => 'principals/' . $result[0]['username'], + 'digestHash' => $result[0]['digesta1'], + ); + if ($result[0]['email']) $user['{http://sabredav.org/ns}email-address'] = $result[0]['email']; + return $user; + + } + + /** + * Returns a list of all users + * + * @return array + */ + public function getUsers() { + + $result = $this->pdo->query('SELECT username, email FROM users')->fetchAll(); + + $rv = array(); + foreach($result as $user) { + + $r = array( + 'uri' => 'principals/' . $user['username'], + ); + if ($user['email']) $r['{http://sabredav.org/ns}email-address'] = $user['email']; + $rv[] = $r; + + } + + return $rv; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php new file mode 100755 index 00000000..cc4f25f3 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php @@ -0,0 +1,434 @@ +authBackend = $authBackend; + $this->realm = $realm; + + } + + /** + * Initializes the plugin. This function is automatically called by the server + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10); + $this->server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + $this->server->subscribeEvent('report',array($this,'report')); + + } + + /** + * This method intercepts calls to PROPFIND and similar lookups + * + * This is done to inject the current-user-principal if this is requested. + * + * @todo support for 'unauthenticated' + * @return void + */ + public function afterGetProperties($href, &$properties) { + + if (array_key_exists('{DAV:}current-user-principal', $properties[404])) { + if ($ui = $this->authBackend->getCurrentUser()) { + $properties[200]['{DAV:}current-user-principal'] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::HREF, $ui['uri']); + } else { + $properties[200]['{DAV:}current-user-principal'] = new Sabre_DAV_Property_Principal(Sabre_DAV_Property_Principal::UNAUTHENTICATED); + } + unset($properties[404]['{DAV:}current-user-principal']); + } + if (array_key_exists('{DAV:}principal-collection-set', $properties[404])) { + $properties[200]['{DAV:}principal-collection-set'] = new Sabre_DAV_Property_Href('principals'); + unset($properties[404]['{DAV:}principal-collection-set']); + } + if (array_key_exists('{DAV:}supported-report-set', $properties[200])) { + $properties[200]['{DAV:}supported-report-set']->addReport(array( + '{DAV:}expand-property', + )); + } + + + } + + /** + * This method is called before any HTTP method and forces users to be authenticated + * + * @param string $method + * @throws Sabre_DAV_Exception_NotAuthenticated + * @return bool + */ + public function beforeMethod($method, $uri) { + + $this->authBackend->authenticate($this->server,$this->realm); + + } + + /** + * This functions handles REPORT requests + * + * @param string $reportName + * @param DOMNode $dom + * @return bool|null + */ + public function report($reportName,$dom) { + + switch($reportName) { + case '{DAV:}expand-property' : + $this->expandPropertyReport($dom); + return false; + case '{DAV:}principal-property-search' : + if ($this->server->getRequestUri()==='principals') { + $this->principalPropertySearchReport($dom); + return false; + } + break; + case '{DAV:}principal-search-property-set' : + if ($this->server->getRequestUri()==='principals') { + $this->principalSearchPropertySetReport($dom); + return false; + } + break; + + } + + } + + /** + * The expand-property report is defined in RFC3253 section 3-8. + * + * This report is very similar to a standard PROPFIND. The difference is + * that it has the additional ability to look at properties containing a + * {DAV:}href element, follow that property and grab additional elements + * there. + * + * Other rfc's, such as ACL rely on this report, so it made sense to put + * it in this plugin. + * + * @param DOMElement $dom + * @return void + */ + protected function expandPropertyReport($dom) { + + $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild); + $depth = $this->server->getHTTPDepth(0); + $requestUri = $this->server->getRequestUri(); + + $result = $this->expandProperties($requestUri,$requestedProperties,$depth); + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $multiStatus = $dom->createElement('d:multistatus'); + $dom->appendChild($multiStatus); + + // Adding in default namespaces + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); + + } + + foreach($result as $entry) { + + $entry->serialize($this->server,$multiStatus); + + } + + $xml = $dom->saveXML(); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->sendBody($xml); + + // Make sure the event chain is broken + return false; + + } + + /** + * This method is used by expandPropertyReport to parse + * out the entire HTTP request. + * + * @param DOMElement $node + * @return array + */ + protected function parseExpandPropertyReportRequest($node) { + + $requestedProperties = array(); + do { + + if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue; + + if ($node->firstChild) { + + $children = $this->parseExpandPropertyReportRequest($node->firstChild); + + } else { + + $children = array(); + + } + + $namespace = $node->getAttribute('namespace'); + if (!$namespace) $namespace = 'DAV:'; + + $propName = '{'.$namespace.'}' . $node->getAttribute('name'); + $requestedProperties[$propName] = $children; + + } while ($node = $node->nextSibling); + + return $requestedProperties; + + } + + /** + * This method expands all the properties and returns + * a list with property values + * + * @param array $path + * @param array $requestedProperties the list of required properties + * @param array $depth + */ + protected function expandProperties($path,array $requestedProperties,$depth) { + + $foundProperties = $this->server->getPropertiesForPath($path,array_keys($requestedProperties),$depth); + + $result = array(); + + foreach($foundProperties as $node) { + + foreach($requestedProperties as $propertyName=>$childRequestedProperties) { + + // We're only traversing if sub-properties were requested + if(count($childRequestedProperties)===0) continue; + + // We only have to do the expansion if the property was found + // and it contains an href element. + if (!array_key_exists($propertyName,$node[200])) continue; + if (!($node[200][$propertyName] instanceof Sabre_DAV_Property_IHref)) continue; + + $href = $node[200][$propertyName]->getHref(); + list($node[200][$propertyName]) = $this->expandProperties($href,$childRequestedProperties,0); + + } + $result[] = new Sabre_DAV_Property_Response($path, $node); + + } + + return $result; + + } + + protected function principalSearchPropertySetReport(DOMDocument $dom) { + + $searchProperties = array( + '{DAV:}displayname' => 'display name' + + ); + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth!==0) { + throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); + } + + if ($dom->firstChild->hasChildNodes()) + throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + $root = $dom->createElement('d:principal-search-property-set'); + $dom->appendChild($root); + // Adding in default namespaces + foreach($this->server->xmlNamespaces as $namespace=>$prefix) { + + $root->setAttribute('xmlns:' . $prefix,$namespace); + + } + + $nsList = $this->server->xmlNamespaces; + + foreach($searchProperties as $propertyName=>$description) { + + $psp = $dom->createElement('d:principal-search-property'); + $root->appendChild($psp); + + $prop = $dom->createElement('d:prop'); + $psp->appendChild($prop); + + $propName = null; + preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); + + //if (!isset($nsList[$propName[1]])) { + // $nsList[$propName[1]] = 'x' . count($nsList); + //} + + // If the namespace was defined in the top-level xml namespaces, it means + // there was already a namespace declaration, and we don't have to worry about it. + //if (isset($server->xmlNamespaces[$propName[1]])) { + $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]); + //} else { + // $currentProperty = $dom->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); + //} + $prop->appendChild($currentProperty); + + $descriptionElem = $dom->createElement('d:description'); + $descriptionElem->setAttribute('xml:lang','en'); + $descriptionElem->appendChild($dom->createTextNode($description)); + $psp->appendChild($descriptionElem); + + + } + + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->sendBody($dom->saveXML()); + + } + + protected function principalPropertySearchReport($dom) { + + $searchableProperties = array( + '{DAV:}displayname' => 'display name' + + ); + + list($searchProperties, $requestedProperties) = $this->parsePrincipalPropertySearchReportRequest($dom); + + $uri = $this->server->getRequestUri(); + + $result = array(); + + $lookupResults = $this->server->getPropertiesForPath($uri, array_keys($searchProperties), 1); + + // The first item in the results is the parent, so we get rid of it. + array_shift($lookupResults); + + $matches = array(); + + foreach($lookupResults as $lookupResult) { + + foreach($searchProperties as $searchProperty=>$searchValue) { + if (!isset($searchableProperties[$searchProperty])) { + throw new Sabre_DAV_Exception_BadRequest('Searching for ' . $searchProperty . ' is not supported'); + } + + if (isset($lookupResult[200][$searchProperty]) && + mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8')!==false) { + $matches[] = $lookupResult['href']; + } + + } + + } + + $matchProperties = array(); + + foreach($matches as $match) { + + list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0); + $matchProperties[] = $result; + + } + + $xml = $this->server->generateMultiStatus($matchProperties); + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->sendStatus(207); + $this->server->httpResponse->sendBody($xml); + + } + + protected function parsePrincipalPropertySearchReportRequest($dom) { + + $httpDepth = $this->server->getHTTPDepth(0); + if ($httpDepth!==0) { + throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); + } + + $searchProperties = array(); + + // Parsing the search request + foreach($dom->firstChild->childNodes as $searchNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search') + continue; + + $propertyName = null; + $propertyValue = null; + + foreach($searchNode->childNodes as $childNode) { + + switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) { + + case '{DAV:}prop' : + $property = Sabre_DAV_XMLUtil::parseProperties($searchNode); + reset($property); + $propertyName = key($property); + break; + + case '{DAV:}match' : + $propertyValue = $childNode->textContent; + break; + + } + + + } + + if (is_null($propertyName) || is_null($propertyValue)) + throw new Sabre_DAV_Exception_BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); + + $searchProperties[$propertyName] = $propertyValue; + + } + + return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild))); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php new file mode 100755 index 00000000..a69ce6f6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php @@ -0,0 +1,147 @@ +principalUri = $principalUri; + $this->principalProperties = $principalProperties; + + } + + /** + * Returns the name of the element + * + * @return void + */ + public function getName() { + + list(, $name) = Sabre_DAV_URLUtil::splitPath($this->principalUri); + return $name; + + } + + /** + * Returns the name of the user + * + * @return void + */ + public function getDisplayName() { + + if (isset($this->principalProperties['{DAV:}displayname'])) { + return $this->principalProperties['{DAV:}displayname']; + } else { + return $this->getName(); + } + + } + + /** + * Returns a list of properties + * + * @param array $requestedProperties + * @return void + */ + public function getProperties($requestedProperties) { + + if (!count($requestedProperties)) { + + // If all properties were requested + // we will only returns properties from this list + $requestedProperties = array( + '{DAV:}resourcetype', + '{DAV:}displayname', + ); + + } + + // We need to always return the resourcetype + // This is a bug in the core server, but it is easier to do it this way for now + $newProperties = array( + '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType('{DAV:}principal') + ); + foreach($requestedProperties as $propName) switch($propName) { + + case '{DAV:}alternate-URI-set' : + if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) { + $href = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']; + $newProperties[$propName] = new Sabre_DAV_Property_Href($href); + } else { + $newProperties[$propName] = null; + } + break; + case '{DAV:}group-member-set' : + case '{DAV:}group-membership' : + $newProperties[$propName] = null; + break; + + case '{DAV:}principal-URL' : + $newProperties[$propName] = new Sabre_DAV_Property_Href($this->principalUri); + break; + + case '{DAV:}displayname' : + $newProperties[$propName] = $this->getDisplayName(); + break; + + default : + if (isset($this->principalProperties[$propName])) { + $newProperties[$propName] = $this->principalProperties[$propName]; + } + break; + + } + + return $newProperties; + + + } + + /** + * Updates this principals properties. + * + * Currently this is not supported + * + * @param array $properties + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateProperties($properties) { + + return false; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php new file mode 100755 index 00000000..75cf3af9 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php @@ -0,0 +1,74 @@ +authBackend = $authBackend; + + } + + /** + * Returns the name of this collection. + * + * @return string + */ + public function getName() { + + return self::NODENAME; + + } + + /** + * Retursn the list of users + * + * @return void + */ + public function getChildren() { + + $children = array(); + foreach($this->authBackend->getUsers() as $principalInfo) { + + $principalUri = $principalInfo['uri'] . '/'; + $children[] = new Sabre_DAV_Auth_Principal($principalUri,$principalInfo); + + + } + return $children; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php new file mode 100755 index 00000000..430ebde8 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php @@ -0,0 +1,97 @@ + 'image/jpeg', + 'gif' => 'image/gif', + 'png' => 'image/png', + + // groupware + 'ics' => 'text/calendar', + 'vcf' => 'text/x-vcard', + + // text + 'txt' => 'text/plain', + + ); + + /** + * Initializes the plugin + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + // Using a relatively low priority (200) to allow other extensions + // to set the content-type first. + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200); + + } + + /** + * Handler for teh afterGetProperties event + * + * @param string $path + * @param array $properties + * @return void + */ + public function afterGetProperties($path, &$properties) { + + if (array_key_exists('{DAV:}getcontenttype', $properties[404])) { + + list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path); + $contentType = $this->getContentType($fileName); + + if ($contentType) { + $properties[200]['{DAV:}getcontenttype'] = $contentType; + unset($properties[404]['{DAV:}getcontenttype']); + } + + } + + } + + /** + * Simple method to return the contenttype + * + * @param string $fileName + * @return string + */ + protected function getContentType($fileName) { + + // Just grabbing the extension + $extension = substr($fileName,strrpos($fileName,'.')+1); + if (isset($this->extensionMap[$extension])) + return $this->extensionMap[$extension]; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php new file mode 100755 index 00000000..df60ae43 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php @@ -0,0 +1,54 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + } + + /** + * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request + * + * @param string $method + * @return bool + */ + public function httpGetInterceptor($method, $uri) { + + if ($method!='GET') return true; + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_IFile) return; + + $this->server->invokeMethod('PROPFIND',$uri); + return false; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php new file mode 100755 index 00000000..f718b4fe --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php @@ -0,0 +1,248 @@ +enablePost = $enablePost; + + } + + /** + * Initializes the plugin and subscribes to events + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor')); + if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler')); + } + + /** + * This method intercepts GET requests to collections and returns the html + * + * @param string $method + * @return bool + */ + public function httpGetInterceptor($method, $uri) { + + if ($method!='GET') return true; + + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_IFile) return true; + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8'); + + $this->server->httpResponse->sendBody( + $this->generateDirectoryIndex($uri) + ); + + return false; + + } + + /** + * Handles POST requests for tree operations + * + * This method is not yet used. + * + * @param string $method + * @return bool + */ + public function httpPOSTHandler($method, $uri) { + + if ($method!='POST') return true; + if (isset($_POST['action'])) switch($_POST['action']) { + + case 'mkcol' : + if (isset($_POST['name']) && trim($_POST['name'])) { + // Using basename() because we won't allow slashes + list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($_POST['name'])); + $this->server->createDirectory($uri . '/' . $folderName); + } + break; + case 'put' : + if ($_FILES) $file = current($_FILES); + else break; + $newName = trim($file['name']); + list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name'])); + if (isset($_POST['name']) && trim($_POST['name'])) + $newName = trim($_POST['name']); + + // Making sure we only have a 'basename' component + list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName); + + + if (is_uploaded_file($file['tmp_name'])) { + $parent = $this->server->tree->getNodeForPath(trim($uri,'/')); + $parent->createFile($newName,fopen($file['tmp_name'],'r')); + } + + } + $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri()); + return false; + + } + + /** + * Escapes a string for html. + * + * @param string $value + * @return void + */ + public function escapeHTML($value) { + + return htmlspecialchars($value,ENT_QUOTES,'UTF-8'); + + } + + /** + * Generates the html directory index for a given url + * + * @param string $path + * @return string + */ + public function generateDirectoryIndex($path) { + + $html = " + + Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . " + + + +

                        Index for " . $this->escapeHTML($path) . "/

                        + + + "; + + $files = $this->server->getPropertiesForPath($path,array( + '{DAV:}displayname', + '{DAV:}resourcetype', + '{DAV:}getcontenttype', + '{DAV:}getcontentlength', + '{DAV:}getlastmodified', + ),1); + + foreach($files as $k=>$file) { + + // This is the current directory, we can skip it + if (rtrim($file['href'],'/')==$path) continue; + + list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']); + + $type = null; + + if (isset($file[200]['{DAV:}resourcetype'])) { + $type = $file[200]['{DAV:}resourcetype']->getValue(); + + // resourcetype can have multiple values + if (is_array($type)) { + $type = implode(', ', $type); + } + + // Some name mapping is preferred + switch($type) { + case '{DAV:}collection' : + $type = 'Collection'; + break; + } + } + + // If no resourcetype was found, we attempt to use + // the contenttype property + if (!$type && isset($file[200]['{DAV:}getcontenttype'])) { + $type = $file[200]['{DAV:}getcontenttype']; + } + if (!$type) $type = 'Unknown'; + + $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:''; + $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):''; + + $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/')); + + $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name; + + $name = $this->escapeHTML($name); + $displayName = $this->escapeHTML($displayName); + $type = $this->escapeHTML($type); + + $html.= " + + + + +"; + + } + + $html.= ""; + + if ($this->enablePost) { + $html.= ''; + } + + $html.= "
                        NameTypeSizeLast modified

                        {$displayName}{$type}{$size}{$lastmodified}

                        +

                        Create new folder

                        + + Name:
                        + +
                        +
                        +

                        Upload file

                        + + Name (optional):
                        + File:
                        + +
                        +
                        +
                        Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2010 http://code.google.com/p/sabredav/
                        + +"; + + return $html; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php b/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php new file mode 100755 index 00000000..ebc266e1 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php @@ -0,0 +1,90 @@ +getChildren() as $child) { + + if ($child->getName()==$name) return $child; + + } + throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name); + + } + + /** + * Checks is a child-node exists. + * + * It is generally a good idea to try and override this. Usually it can be optimized. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + try { + + $this->getChild($name); + return true; + + } catch(Sabre_DAV_Exception_FileNotFound $e) { + + return false; + + } + + } + + /** + * Creates a new file in the directory + * + * @param string $name Name of the file + * @param resource $data Initial payload, passed as a readable stream resource. + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createFile($name, $data = null) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')'); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @throws Sabre_DAV_Exception_Forbidden + * @return void + */ + public function createDirectory($name) { + + throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory'); + + } + + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php new file mode 100755 index 00000000..46b710a0 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php @@ -0,0 +1,63 @@ +lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock'); + $errorNode->appendChild($error); + if (!is_object($this->lock)) var_dump($this->lock); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php new file mode 100755 index 00000000..cd03d4c7 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php @@ -0,0 +1,28 @@ +ownerDocument->createElementNS('DAV:','d:valid-resourcetype'); + $errorNode->appendChild($error); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php new file mode 100755 index 00000000..059ee346 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php @@ -0,0 +1,39 @@ +message = 'The locktoken supplied does not match any locks on this entity'; + + } + + /** + * This method allows the exception to include additonal information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri'); + $errorNode->appendChild($error); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php new file mode 100755 index 00000000..3f15ea74 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php @@ -0,0 +1,67 @@ +lock = $lock; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + public function getHTTPCode() { + + return 423; + + } + + /** + * This method allows the exception to include additonal information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + if ($this->lock) { + $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted'); + $errorNode->appendChild($error); + if (!is_object($this->lock)) var_dump($this->lock); + $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri)); + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php new file mode 100755 index 00000000..67ad9a8a --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php @@ -0,0 +1,44 @@ +getAllowedMethods($server->getRequestUri()); + + return array( + 'Allow' => strtoupper(implode(', ',$methods)), + ); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php new file mode 100755 index 00000000..28159853 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php @@ -0,0 +1,28 @@ +header = $header; + + } + + /** + * Returns the HTTP statuscode for this exception + * + * @return int + */ + public function getHTTPCode() { + + return 412; + + } + + /** + * This method allows the exception to include additonal information into the WebDAV error response + * + * @param Sabre_DAV_Server $server + * @param DOMElement $errorNode + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) { + + if ($this->header) { + $prop = $errorNode->ownerDocument->createElement('s:header'); + $prop->nodeValue = $this->header; + $errorNode->appendChild($prop); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php new file mode 100755 index 00000000..64526f42 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php @@ -0,0 +1,30 @@ +ownerDocument->createElementNS('DAV:','d:supported-report'); + $errorNode->appendChild($error); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php new file mode 100755 index 00000000..723138d8 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php @@ -0,0 +1,29 @@ +path . '/' . $name; + file_put_contents($newPath,$data); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + $newPath = $this->path . '/' . $name; + mkdir($newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File with name ' . $path . ' could not be located'); + + if (is_dir($path)) { + + return new Sabre_DAV_FS_Directory($path); + + } else { + + return new Sabre_DAV_FS_File($path); + + } + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $nodes = array(); + foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node); + return $nodes; + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return void + */ + public function delete() { + + foreach($this->getChildren() as $child) $child->delete(); + rmdir($this->path); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + public function getQuotaInfo() { + + return array( + disk_total_space($this->path)-disk_free_space($this->path), + disk_free_space($this->path) + ); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php new file mode 100755 index 00000000..dc2e7f98 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php @@ -0,0 +1,89 @@ +path,$data); + + } + + /** + * Returns the data + * + * @return string + */ + public function get() { + + return fopen($this->path,'r'); + + } + + /** + * Delete the current file + * + * @return void + */ + public function delete() { + + unlink($this->path); + + } + + /** + * Returns the size of the node, in bytes + * + * @return int + */ + public function getSize() { + + return filesize($this->path); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + * + * @return mixed + */ + public function getETag() { + + return null; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + * + * @return mixed + */ + public function getContentType() { + + return null; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php b/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php new file mode 100755 index 00000000..172af852 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php @@ -0,0 +1,81 @@ +path = $path; + + } + + + + /** + * Returns the name of the node + * + * @return string + */ + public function getName() { + + list(, $name) = Sabre_DAV_URLUtil::splitPath($this->path); + return $name; + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) { + + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); + + $newPath = $parentPath . '/' . $newName; + rename($this->path,$newPath); + + $this->path = $newPath; + + } + + + + /** + * Returns the last modification time, as a unix timestamp + * + * @return int + */ + public function getLastModified() { + + return filemtime($this->path); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php new file mode 100755 index 00000000..b42a9a9d --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php @@ -0,0 +1,135 @@ +path . '/' . $name; + file_put_contents($newPath,$data); + + } + + /** + * Creates a new subdirectory + * + * @param string $name + * @return void + */ + public function createDirectory($name) { + + // We're not allowing dots + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + $newPath = $this->path . '/' . $name; + mkdir($newPath); + + } + + /** + * Returns a specific child node, referenced by its name + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + $path = $this->path . '/' . $name; + + if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File could not be located'); + if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + + if (is_dir($path)) { + + return new Sabre_DAV_FSExt_Directory($path); + + } else { + + return new Sabre_DAV_FSExt_File($path); + + } + + } + + /** + * Checks if a child exists. + * + * @param string $name + * @return bool + */ + public function childExists($name) { + + if ($name=='.' || $name=='..') + throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); + + $path = $this->path . '/' . $name; + return file_exists($path); + + } + + /** + * Returns an array with all the child nodes + * + * @return Sabre_DAV_INode[] + */ + public function getChildren() { + + $nodes = array(); + foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node); + return $nodes; + + } + + /** + * Deletes all files in this directory, and then itself + * + * @return void + */ + public function delete() { + + // Deleting all children + foreach($this->getChildren() as $child) $child->delete(); + + // Removing resource info, if its still around + if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav'); + + // Removing the directory itself + rmdir($this->path); + + return parent::delete(); + + } + + /** + * Returns available diskspace information + * + * @return array + */ + public function getQuotaInfo() { + + return array( + disk_total_space($this->path)-disk_free_space($this->path), + disk_free_space($this->path) + ); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php new file mode 100755 index 00000000..987bca33 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php @@ -0,0 +1,88 @@ +path,$data); + + } + + /** + * Returns the data + * + * @return string + */ + public function get() { + + return fopen($this->path,'r'); + + } + + /** + * Delete the current file + * + * @return void + */ + public function delete() { + + unlink($this->path); + return parent::delete(); + + } + + /** + * Returns the ETag for a file + * + * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change. + * The ETag is an arbritrary string, but MUST be surrounded by double-quotes. + * + * Return null if the ETag can not effectively be determined + */ + public function getETag() { + + return '"' . md5_file($this->path). '"'; + + } + + /** + * Returns the mime-type for a file + * + * If null is returned, we'll assume application/octet-stream + */ + public function getContentType() { + + return null; + + } + + /** + * Returns the size of the file, in bytes + * + * @return int + */ + public function getSize() { + + return filesize($this->path); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php new file mode 100755 index 00000000..0af346d1 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php @@ -0,0 +1,276 @@ +getResourceData(); + $locks = $resourceData['locks']; + foreach($locks as $k=>$lock) { + if (time() > $lock->timeout + $lock->created) unset($locks[$k]); + } + return $locks; + + } + + /** + * Locks this node + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return void + */ + function lock(Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + + $resourceData = $this->getResourceData(); + if (!isset($resourceData['locks'])) $resourceData['locks'] = array(); + $current = null; + foreach($resourceData['locks'] as $k=>$lock) { + if ($lock->token === $lockInfo->token) $current = $k; + } + if (!is_null($current)) $resourceData['locks'][$current] = $lockInfo; + else $resourceData['locks'][] = $lockInfo; + + $this->putResourceData($resourceData); + + } + + /** + * Removes a lock from this node + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + function unlock(Sabre_DAV_Locks_LockInfo $lockInfo) { + + //throw new Sabre_DAV_Exception('bla'); + $resourceData = $this->getResourceData(); + foreach($resourceData['locks'] as $k=>$lock) { + + if ($lock->token === $lockInfo->token) { + + unset($resourceData['locks'][$k]); + $this->putResourceData($resourceData); + return true; + + } + } + return false; + + } + + /** + * Updates properties on this node, + * + * @param array $mutations + * @see Sabre_DAV_IProperties::updateProperties + * @return bool|array + */ + public function updateProperties($properties) { + + $resourceData = $this->getResourceData(); + + $result = array(); + + foreach($properties as $propertyName=>$propertyValue) { + + // If it was null, we need to delete the property + if (is_null($propertyValue)) { + if (isset($resourceData['properties'][$propertyName])) { + unset($resourceData['properties'][$propertyName]); + } + } else { + $resourceData['properties'][$propertyName] = $propertyValue; + } + + } + + $this->putResourceData($resourceData); + return true; + } + + /** + * Returns a list of properties for this nodes.; + * + * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author + * If the array is empty, all properties should be returned + * + * @param array $properties + * @return void + */ + function getProperties($properties) { + + $resourceData = $this->getResourceData(); + + // if the array was empty, we need to return everything + if (!$properties) return $resourceData['properties']; + + $props = array(); + foreach($properties as $property) { + if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property]; + } + + return $props; + + } + + /** + * Returns the path to the resource file + * + * @return string + */ + protected function getResourceInfoPath() { + + list($parentDir) = Sabre_DAV_URLUtil::splitPath($this->path); + return $parentDir . '/.sabredav'; + + } + + /** + * Returns all the stored resource information + * + * @return array + */ + protected function getResourceData() { + + $path = $this->getResourceInfoPath(); + if (!file_exists($path)) return array('locks'=>array(), 'properties' => array()); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'r'); + flock($handle,LOCK_SH); + $data = ''; + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!isset($data[$this->getName()])) { + return array('locks'=>array(), 'properties' => array()); + } + + $data = $data[$this->getName()]; + if (!isset($data['locks'])) $data['locks'] = array(); + if (!isset($data['properties'])) $data['properties'] = array(); + return $data; + + } + + /** + * Updates the resource information + * + * @param array $newData + * @return void + */ + protected function putResourceData(array $newData) { + + $path = $this->getResourceInfoPath(); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + $data = ''; + + rewind($handle); + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + $data[$this->getName()] = $newData; + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($data)); + fclose($handle); + + } + + /** + * Renames the node + * + * @param string $name The new name + * @return void + */ + public function setName($name) { + + list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path); + list(, $newName) = Sabre_DAV_URLUtil::splitPath($name); + $newPath = $parentPath . '/' . $newName; + + // We're deleting the existing resourcedata, and recreating it + // for the new path. + $resourceData = $this->getResourceData(); + $this->deleteResourceData(); + + rename($this->path,$newPath); + $this->path = $newPath; + $this->putResourceData($resourceData); + + + } + + public function deleteResourceData() { + + // When we're deleting this node, we also need to delete any resource information + $path = $this->getResourceInfoPath(); + if (!file_exists($path)) return true; + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + $data = ''; + + rewind($handle); + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (isset($data[$this->getName()])) unset($data[$this->getName()]); + ftruncate($handle,0); + rewind($handle); + fwrite($handle,serialize($data)); + fclose($handle); + + } + + public function delete() { + + return $this->deleteResourceData(); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/File.php b/3.0/modules/webdav/libraries/Sabre/DAV/File.php new file mode 100755 index 00000000..aaaf88e6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/File.php @@ -0,0 +1,81 @@ + array( + * '{DAV:}displayname' => null, + * ), + * 424 => array( + * '{DAV:}owner' => null, + * ) + * ) + * + * In this example it was forbidden to update {DAV:}displayname. + * (403 Forbidden), which in turn also caused {DAV:}owner to fail + * (424 Failed Dependency) because the request needs to be atomic. + * + * @param array $mutations + * @return bool|array + */ + function updateProperties($properties); + + /** + * Returns a list of properties for this nodes. + * + * The properties list is a list of propertynames the client requested, + * encoded in clark-notation {xmlnamespace}tagname + * + * If the array is empty, it means 'all properties' were requested. + * + * @param array $properties + * @return void + */ + function getProperties($properties); + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php b/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php new file mode 100755 index 00000000..afba5efd --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php @@ -0,0 +1,27 @@ +dataDir = $dataDir; + + } + + protected function getFileNameForUri($uri) { + + return $this->dataDir . '/sabredav_' . md5($uri) . '.locks'; + + } + + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * @param string $uri + * @return array + */ + public function getLocks($uri) { + + $lockList = array(); + $currentPath = ''; + + foreach(explode('/',$uri) as $uriPart) { + + // weird algorithm that can probably be improved, but we're traversing the path top down + if ($currentPath) $currentPath.='/'; + $currentPath.=$uriPart; + + $uriLocks = $this->getData($currentPath); + + foreach($uriLocks as $uriLock) { + + // Unless we're on the leaf of the uri-tree we should ingore locks with depth 0 + if($uri==$currentPath || $uriLock->depth!=0) { + $uriLock->uri = $currentPath; + $lockList[] = $uriLock; + } + + } + + } + + // Checking if we can remove any of these locks + foreach($lockList as $k=>$lock) { + if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); + } + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 1800; + $lockInfo->created = time(); + + $locks = $this->getLocks($uri); + foreach($locks as $k=>$lock) { + if ($lock->token == $lockInfo->token) unset($locks[$k]); + } + $locks[] = $lockInfo; + $this->putData($uri,$locks); + return true; + + } + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + $locks = $this->getLocks($uri); + foreach($locks as $k=>$lock) { + + if ($lock->token == $lockInfo->token) { + + unset($locks[$k]); + $this->putData($uri,$locks); + return true; + + } + } + return false; + + } + + /** + * Returns the stored data for a uri + * + * @param string $uri + * @return array + */ + protected function getData($uri) { + + $path = $this->getFilenameForUri($uri); + if (!file_exists($path)) return array(); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'r'); + flock($handle,LOCK_SH); + $data = ''; + + // Reading data until the eof + while(!feof($handle)) { + $data.=fread($handle,8192); + } + + // We're all good + fclose($handle); + + // Unserializing and checking if the resource file contains data for this file + $data = unserialize($data); + if (!$data) return array(); + return $data; + + } + + /** + * Updates the lock information + * + * @param string $uri + * @param array $newData + * @return void + */ + protected function putData($uri,array $newData) { + + $path = $this->getFileNameForUri($uri); + + // opening up the file, and creating a shared lock + $handle = fopen($path,'a+'); + flock($handle,LOCK_EX); + ftruncate($handle,0); + rewind($handle); + + fwrite($handle,serialize($newData)); + fclose($handle); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php new file mode 100755 index 00000000..11c7fa96 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php @@ -0,0 +1,141 @@ +pdo = $pdo; + + } + + /** + * Returns a list of Sabre_DAV_Locks_LockInfo objects + * + * This method should return all the locks for a particular uri, including + * locks that might be set on a parent uri. + * + * @param string $uri + * @return array + */ + public function getLocks($uri) { + + // NOTE: the following 10 lines or so could be easily replaced by + // pure sql. MySQL's non-standard string concatination prevents us + // from doing this though. + $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM locks WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)'; + $params = array(time(),$uri); + + // We need to check locks for every part in the uri. + $uriParts = explode('/',$uri); + + // We already covered the last part of the uri + array_pop($uriParts); + + $currentPath=''; + + foreach($uriParts as $part) { + + if ($currentPath) $currentPath.='/'; + $currentPath.=$part; + + $query.=' OR (depth!=0 AND uri = ?)'; + $params[] = $currentPath; + + } + + $query.=')'; + + $stmt = $this->pdo->prepare($query); + $stmt->execute($params); + $result = $stmt->fetchAll(); + + $lockList = array(); + foreach($result as $row) { + + $lockInfo = new Sabre_DAV_Locks_LockInfo(); + $lockInfo->owner = $row['owner']; + $lockInfo->token = $row['token']; + $lockInfo->timeout = $row['timeout']; + $lockInfo->created = $row['created']; + $lockInfo->scope = $row['scope']; + $lockInfo->depth = $row['depth']; + $lockInfo->uri = $row['uri']; + $lockList[] = $lockInfo; + + } + + return $lockList; + + } + + /** + * Locks a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + // We're making the lock timeout 30 minutes + $lockInfo->timeout = 30*60; + $lockInfo->created = time(); + $lockInfo->uri = $uri; + + $locks = $this->getLocks($uri); + $exists = false; + foreach($locks as $k=>$lock) { + if ($lock->token == $lockInfo->token) $exists = true; + } + + if ($exists) { + $stmt = $this->pdo->prepare('UPDATE locks SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?'); + $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); + } else { + $stmt = $this->pdo->prepare('INSERT INTO locks (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)'); + $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token)); + } + + return true; + + } + + + + /** + * Removes a lock from a uri + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return bool + */ + public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + $stmt = $this->pdo->prepare('DELETE FROM locks WHERE uri = ? AND token = ?'); + $stmt->execute(array($uri,$lockInfo->token)); + + return $stmt->rowCount()===1; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php new file mode 100755 index 00000000..aa174384 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php @@ -0,0 +1,81 @@ +addPlugin($lockPlugin); + * + * @package Sabre + * @subpackage DAV + * @copyright Copyright (C) 2007-2010 Rooftop Solutions. All rights reserved. + * @author Evert Pot (http://www.rooftopsolutions.nl/) + * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License + */ +class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin { + + /** + * locksBackend + * + * @var Sabre_DAV_Locks_Backend_Abstract + */ + private $locksBackend; + + /** + * server + * + * @var Sabre_DAV_Server + */ + private $server; + + /** + * __construct + * + * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend + * @return void + */ + public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) { + + $this->locksBackend = $locksBackend; + + } + + /** + * Initializes the plugin + * + * This method is automatically called by the Server class after addPlugin. + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + + $this->server = $server; + $server->subscribeEvent('unknownMethod',array($this,'unknownMethod')); + $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50); + $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties')); + + } + + /** + * This method is called by the Server if the user used an HTTP method + * the server didn't recognize. + * + * This plugin intercepts the LOCK and UNLOCK methods. + * + * @param string $method + * @return bool + */ + public function unknownMethod($method, $uri) { + + switch($method) { + + case 'LOCK' : $this->httpLock($uri); return false; + case 'UNLOCK' : $this->httpUnlock($uri); return false; + + } + + } + + /** + * This method is called after most properties have been found + * it allows us to add in any Lock-related properties + * + * @param string $path + * @param array $properties + * @return bool + */ + public function afterGetProperties($path,&$newProperties) { + + foreach($newProperties[404] as $propName=>$discard) { + + $node = null; + + switch($propName) { + + case '{DAV:}supportedlock' : + $val = false; + if ($this->locksBackend) $val = true; + else { + if (!$node) $node = $this->server->tree->getNodeForPath($path); + if ($node instanceof Sabre_DAV_ILockable) $val = true; + } + $newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val); + unset($newProperties[404][$propName]); + break; + + case '{DAV:}lockdiscovery' : + $newProperties[200][$propName] = new Sabre_DAV_Property_LockDiscovery($this->getLocks($path)); + unset($newProperties[404][$propName]); + break; + + } + + + } + return true; + + } + + + /** + * This method is called before the logic for any HTTP method is + * handled. + * + * This plugin uses that feature to intercept access to locked resources. + * + * @param string $method + * @param string $uri + * @return bool + */ + public function beforeMethod($method, $uri) { + + switch($method) { + + case 'DELETE' : + case 'MKCOL' : + case 'PROPPATCH' : + case 'PUT' : + $lastLock = null; + if (!$this->validateLock($uri,$lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'MOVE' : + $lastLock = null; + if (!$this->validateLock(array( + $uri, + $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), + ),$lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + case 'COPY' : + $lastLock = null; + if (!$this->validateLock( + $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')), + $lastLock)) + throw new Sabre_DAV_Exception_Locked($lastLock); + break; + } + + return true; + + } + + + /** + * Use this method to tell the server this plugin defines additional + * HTTP methods. + * + * This method is passed a uri. It should only return HTTP methods that are + * available for the specified uri. + * + * @param string $uri + * @return array + */ + public function getHTTPMethods($uri) { + + if ($this->locksBackend || + $this->server->tree->getNodeForPath($uri) instanceof Sabre_DAV_ILocks) { + return array('LOCK','UNLOCK'); + } + return array(); + + } + + /** + * Returns a list of features for the HTTP OPTIONS Dav: header. + * + * In this case this is only the number 2. The 2 in the Dav: header + * indicates the server supports locks. + * + * @return array + */ + public function getFeatures() { + + return array(2); + + } + + /** + * Returns all lock information on a particular uri + * + * This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array. + * + * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree + * + * @param string $uri + * @return array + */ + public function getLocks($uri) { + + $lockList = array(); + $currentPath = ''; + foreach(explode('/',$uri) as $uriPart) { + + $uriLocks = array(); + if ($currentPath) $currentPath.='/'; + $currentPath.=$uriPart; + + try { + + $node = $this->server->tree->getNodeForPath($currentPath); + if ($node instanceof Sabre_DAV_ILockable) $uriLocks = $node->getLocks(); + + } catch (Sabre_DAV_Exception_FileNotFound $e){ + // In case the node didn't exist, this could be a lock-null request + } + + foreach($uriLocks as $uriLock) { + + // Unless we're on the leaf of the uri-tree we should ingore locks with depth 0 + if($uri==$currentPath || $uriLock->depth!=0) { + $uriLock->uri = $currentPath; + $lockList[] = $uriLock; + } + + } + + } + if ($this->locksBackend) $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri)); + return $lockList; + + } + + /** + * Locks an uri + * + * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock + * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type + * of lock (shared or exclusive) and the owner of the lock + * + * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock + * + * Additionally, a lock can be requested for a non-existant file. In these case we're obligated to create an empty file as per RFC4918:S7.3 + * + * @param string $uri + * @return void + */ + protected function httpLock($uri) { + + $lastLock = null; + if (!$this->validateLock($uri,$lastLock)) { + + // If the existing lock was an exclusive lock, we need to fail + if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) { + //var_dump($lastLock); + throw new Sabre_DAV_Exception_ConflictingLock($lastLock); + } + + } + + if ($body = $this->server->httpRequest->getBody(true)) { + // This is a new lock request + $lockInfo = $this->parseLockRequest($body); + $lockInfo->depth = $this->server->getHTTPDepth(); + $lockInfo->uri = $uri; + if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock); + + } elseif ($lastLock) { + + // This must have been a lock refresh + $lockInfo = $lastLock; + + // The resource could have been locked through another uri. + if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri; + + } else { + + // There was neither a lock refresh nor a new lock request + throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests'); + + } + + if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout; + + $newFile = false; + + // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first + try { + $node = $this->server->tree->getNodeForPath($uri); + + // We need to call the beforeWriteContent event for RFC3744 + $this->server->broadcastEvent('beforeWriteContent',array($uri)); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + // It didn't, lets create it + $this->server->createFile($uri,fopen('php://memory','r')); + $newFile = true; + + } + + $this->lockNode($uri,$lockInfo); + + $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->server->httpResponse->setHeader('Lock-Token','token . '>'); + $this->server->httpResponse->sendStatus($newFile?201:200); + $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo)); + + } + + /** + * Unlocks a uri + * + * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header + * The server should return 204 (No content) on success + * + * @param string $uri + * @return void + */ + protected function httpUnlock($uri) { + + $lockToken = $this->server->httpRequest->getHeader('Lock-Token'); + + // If the locktoken header is not supplied, we need to throw a bad request exception + if (!$lockToken) throw new Sabre_DAV_Exception_BadRequest('No lock token was supplied'); + + $locks = $this->getLocks($uri); + + // We're grabbing the node information, just to rely on the fact it will throw a 404 when the node doesn't exist + //$this->server->tree->getNodeForPath($uri); + + foreach($locks as $lock) { + + if ('token . '>' == $lockToken) { + + $this->unlockNode($uri,$lock); + $this->server->httpResponse->setHeader('Content-Length','0'); + $this->server->httpResponse->sendStatus(204); + return; + + } + + } + + // If we got here, it means the locktoken was invalid + throw new Sabre_DAV_Exception_LockTokenMatchesRequestUri(); + + } + + /** + * Locks a uri + * + * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored + * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return void + */ + public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return; + + try { + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_ILockable) return $node->lock($lockInfo); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + // In case the node didn't exist, this could be a lock-null request + } + if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo); + throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.'); + + } + + /** + * Unlocks a uri + * + * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified + * + * @param string $uri + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return void + */ + public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) { + + if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return; + try { + $node = $this->server->tree->getNodeForPath($uri); + if ($node instanceof Sabre_DAV_ILockable) return $node->unlock($lockInfo); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + // In case the node didn't exist, this could be a lock-null request + } + + if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo); + + } + + + /** + * Returns the contents of the HTTP Timeout header. + * + * The method formats the header into an integer. + * + * @return int + */ + public function getTimeoutHeader() { + + $header = $this->server->httpRequest->getHeader('Timeout'); + + if ($header) { + + if (stripos($header,'second-')===0) $header = (int)(substr($header,7)); + else if (strtolower($header)=='infinite') $header=Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE; + else throw new Sabre_DAV_Exception_BadRequest('Invalid HTTP timeout header'); + + } else { + + $header = 0; + + } + + return $header; + + } + + /** + * Generates the response for successfull LOCK requests + * + * @param Sabre_DAV_Locks_LockInfo $lockInfo + * @return string + */ + protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) { + + $dom = new DOMDocument('1.0','utf-8'); + $dom->formatOutput = true; + + $prop = $dom->createElementNS('DAV:','d:prop'); + $dom->appendChild($prop); + + $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery'); + $prop->appendChild($lockDiscovery); + + $lockObj = new Sabre_DAV_Property_LockDiscovery(array($lockInfo),true); + $lockObj->serialize($this->server,$lockDiscovery); + + return $dom->saveXML(); + + } + + /** + * validateLock should be called when a write operation is about to happen + * It will check if the requested url is locked, and see if the correct lock tokens are passed + * + * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri + * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo) + * @return bool + */ + protected function validateLock($urls = null,&$lastLock = null) { + + if (is_null($urls)) { + $urls = array($this->server->getRequestUri()); + } elseif (is_string($urls)) { + $urls = array($urls); + } elseif (!is_array($urls)) { + throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array'); + } + + $conditions = $this->getIfConditions(); + + // We're going to loop through the urls and make sure all lock conditions are satisfied + foreach($urls as $url) { + + $locks = $this->getLocks($url); + + // If there were no conditions, but there were locks, we fail + if (!$conditions && $locks) { + reset($locks); + $lastLock = current($locks); + return false; + } + + // If there were no locks or conditions, we go to the next url + if (!$locks && !$conditions) continue; + + foreach($conditions as $condition) { + + $conditionUri = $condition['uri']?$this->server->calculateUri($condition['uri']):''; + + // If the condition has a url, and it isn't part of the affected url at all, check the next condition + if ($conditionUri && strpos($url,$conditionUri)!==0) continue; + + // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken + // At least 1 condition has to be satisfied + foreach($condition['tokens'] as $conditionToken) { + + $etagValid = true; + $lockValid = true; + + // key 2 can contain an etag + if ($conditionToken[2]) { + + $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); + $node = $this->server->tree->getNodeForPath($uri); + $etagValid = $node->getETag()==$conditionToken[2]; + + } + + // key 1 can contain a lock token + if ($conditionToken[1]) { + + $lockValid = false; + // Match all the locks + foreach($locks as $lockIndex=>$lock) { + + $lockToken = 'opaquelocktoken:' . $lock->token; + + // Checking NOT + if (!$conditionToken[0] && $lockToken != $conditionToken[1]) { + + // Condition valid, onto the next + $lockValid = true; + break; + } + if ($conditionToken[0] && $lockToken == $conditionToken[1]) { + + $lastLock = $lock; + // Condition valid and lock matched + unset($locks[$lockIndex]); + $lockValid = true; + break; + + } + + } + + } + + // If, after checking both etags and locks they are stil valid, + // we can continue with the next condition. + if ($etagValid && $lockValid) continue 2; + } + // No conditions matched, so we fail + throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match','If'); + } + + // Conditions were met, we'll also need to check if all the locks are gone + if (count($locks)) { + + reset($locks); + + // There's still locks, we fail + $lastLock = current($locks); + return false; + + } + + + } + + // We got here, this means every condition was satisfied + return true; + + } + + /** + * This method is created to extract information from the WebDAV HTTP 'If:' header + * + * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information + * The function will return an array, containg structs with the following keys + * + * * uri - the uri the condition applies to. This can be an empty string for 'every relevant url' + * * tokens - The lock token. another 2 dimensional array containg 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token) + * * etag - an etag, if supplied + * + * @return void + */ + public function getIfConditions() { + + $header = $this->server->httpRequest->getHeader('If'); + if (!$header) return array(); + + $matches = array(); + + $regex = '/(?:\<(?P.*?)\>\s)?\((?PNot\s)?(?:\<(?P[^\>]*)\>)?(?:\s?)(?:\[(?P[^\]]*)\])?\)/im'; + preg_match_all($regex,$header,$matches,PREG_SET_ORDER); + + $conditions = array(); + + foreach($matches as $match) { + + $condition = array( + 'uri' => $match['uri'], + 'tokens' => array( + array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'') + ), + ); + + if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array( + $match['not']?0:1, + $match['token'], + isset($match['etag'])?$match['etag']:'' + ); + else { + $conditions[] = $condition; + } + + } + + return $conditions; + + } + + /** + * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object + * + * @param string $body + * @return Sabre_DAV_Locks_LockInfo + */ + protected function parseLockRequest($body) { + + $xml = simplexml_load_string($body,null,LIBXML_NOWARNING); + $xml->registerXPathNamespace('d','DAV:'); + $lockInfo = new Sabre_DAV_Locks_LockInfo(); + + $lockInfo->owner = (string)$xml->owner; + + $lockToken = '44445502'; + $id = md5(microtime() . 'somethingrandom'); + $lockToken.='-' . substr($id,0,4) . '-' . substr($id,4,4) . '-' . substr($id,8,4) . '-' . substr($id,12,12); + + $lockInfo->token = $lockToken; + $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0?Sabre_DAV_Locks_LockInfo::EXCLUSIVE:Sabre_DAV_Locks_LockInfo::SHARED; + + return $lockInfo; + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php new file mode 100755 index 00000000..c2dc4296 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php @@ -0,0 +1,79 @@ +server = $server; + $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90); + + } + + /** + * 'beforeMethod' event handles. This event handles intercepts GET requests ending + * with ?mount + * + * @param string $method + * @return void + */ + public function beforeMethod($method, $uri) { + + if ($method!='GET') return; + if ($this->server->httpRequest->getQueryString()!='mount') return; + + $currentUri = $this->server->httpRequest->getAbsoluteUri(); + + // Stripping off everything after the ? + list($currentUri) = explode('?',$currentUri); + + $this->davMount($currentUri); + + // Returning false to break the event chain + return false; + + } + + /** + * Generates the davmount response + * + * @param string $uri absolute uri + * @return void + */ + public function davMount($uri) { + + $this->server->httpResponse->sendStatus(200); + $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml'); + ob_start(); + echo '', "\n"; + echo "\n"; + echo " ", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "\n"; + echo ""; + $this->server->httpResponse->sendBody(ob_get_clean()); + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Node.php b/3.0/modules/webdav/libraries/Sabre/DAV/Node.php new file mode 100755 index 00000000..8943b786 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Node.php @@ -0,0 +1,55 @@ +rootNode = $rootNode; + + } + + /** + * Returns the INode object for the requested path + * + * @param string $path + * @return Sabre_DAV_INode + */ + public function getNodeForPath($path) { + + $path = trim($path,'/'); + if (isset($this->cache[$path])) return $this->cache[$path]; + + //if (!$path || $path=='.') return $this->rootNode; + $currentNode = $this->rootNode; + $i=0; + // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. + foreach(explode('/',$path) as $pathPart) { + + // If this part of the path is just a dot, it actually means we can skip it + if ($pathPart=='.' || $pathPart=='') continue; + + if (!($currentNode instanceof Sabre_DAV_ICollection)) + throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path); + + $currentNode = $currentNode->getChild($pathPart); + + } + + $this->cache[$path] = $currentNode; + return $currentNode; + + } + + /** + * This function allows you to check if a node exists. + * + * @param string $path + * @return bool + */ + public function nodeExists($path) { + + try { + + list($parent, $base) = Sabre_DAV_URLUtil::splitPath($path); + $parentNode = $this->getNodeForPath($parent); + return $parentNode->childExists($base); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + return false; + + } + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + public function getChildren($path) { + + $node = $this->getNodeForPath($path); + $children = $node->getChildren(); + foreach($children as $child) { + + $this->cache[trim($path,'/') . '/' . $child->getName()] = $child; + + } + return $children; + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + public function markDirty($path) { + + // We don't care enough about sub-paths + // flushing the entire cache + $path = trim($path,'/'); + foreach($this->cache as $nodePath=>$node) { + if ($nodePath == $path || strpos($nodePath,$path.'/')===0) + unset($this->cache[$nodePath]); + + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property.php new file mode 100755 index 00000000..ae0a64c6 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property.php @@ -0,0 +1,25 @@ +time = $time; + } elseif (is_int($time) || ctype_digit($time)) { + $this->time = new DateTime('@' . $time); + } else { + $this->time = new DateTime($time); + } + + // Setting timezone to UTC + $this->time->setTimezone(new DateTimeZone('UTC')); + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $prop) { + + $doc = $prop->ownerDocument; + $prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/'); + $prop->setAttribute('b:dt','dateTime.rfc1123'); + $prop->nodeValue = $this->time->format(DateTime::RFC1123); + + } + + /** + * getTime + * + * @return DateTime + */ + public function getTime() { + + return $this->time; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php new file mode 100755 index 00000000..8b9400fb --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php @@ -0,0 +1,91 @@ +href = $href; + $this->autoPrefix = $autoPrefix; + + } + + /** + * Returns the uri + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Serializes this property. + * + * It will additionally prepend the href property with the server's base uri. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + + $prefix = $server->xmlNamespaces['DAV:']; + + $elem = $dom->ownerDocument->createElement($prefix . ':href'); + $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $this->href; + $dom->appendChild($elem); + + } + + /** + * Unserializes this property from a DOM Element + * + * This method returns an instance of this class. + * It will only decode {DAV:}href values. For non-compatible elements null will be returned. + * + * @param DOMElement $dom + * @return Sabre_DAV_Property_Href + */ + static function unserialize(DOMElement $dom) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') { + return new self($dom->firstChild->textContent,false); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php new file mode 100755 index 00000000..56503acd --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php @@ -0,0 +1,25 @@ +locks = $locks; + $this->revealLockToken = $revealLockToken; + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $doc = $prop->ownerDocument; + + foreach($this->locks as $lock) { + + $activeLock = $doc->createElementNS('DAV:','d:activelock'); + $prop->appendChild($activeLock); + + $lockScope = $doc->createElementNS('DAV:','d:lockscope'); + $activeLock->appendChild($lockScope); + + $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==Sabre_DAV_Locks_LockInfo::EXCLUSIVE?'exclusive':'shared'))); + + $lockType = $doc->createElementNS('DAV:','d:locktype'); + $activeLock->appendChild($lockType); + + $lockType->appendChild($doc->createElementNS('DAV:','d:write')); + + $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == Sabre_DAV_Server::DEPTH_INFINITY?'infinity':$lock->depth))); + $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout)); + + if ($this->revealLockToken) { + $lockToken = $doc->createElementNS('DAV:','d:locktoken'); + $activeLock->appendChild($lockToken); + $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token)); + } + + $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner)); + + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php new file mode 100755 index 00000000..df3f7848 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php @@ -0,0 +1,126 @@ +type = $type; + + if ($type===self::HREF && is_null($href)) { + throw new Sabre_DAV_Exception('The href argument must be specified for the HREF principal type.'); + } + $this->href = $href; + + } + + /** + * Returns the principal type + * + * @return int + */ + public function getType() { + + return $this->type; + + } + + /** + * Returns the principal uri. + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Serializes the property into a DOMElement. + * + * @param Sabre_DAV_Server $server + * @param DOMElement $node + * @return void + */ + public function serialize(Sabre_DAV_Server $server, DOMElement $node) { + + $prefix = $server->xmlNamespaces['DAV:']; + switch($this->type) { + + case self::UNAUTHENTICATED : + $node->appendChild( + $node->ownerDocument->createElement($prefix . ':unauthenticated') + ); + break; + case self::AUTHENTICATED : + $node->appendChild( + $node->ownerDocument->createElement($prefix . ':authenticated') + ); + break; + case self::HREF : + $href = $node->ownerDocument->createElement($prefix . ':href'); + $href->nodeValue = $server->getBaseUri() . $this->href; + $node->appendChild($href); + break; + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php new file mode 100755 index 00000000..51022a48 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php @@ -0,0 +1,80 @@ +resourceType = null; + elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY) + $this->resourceType = '{DAV:}collection'; + else + $this->resourceType = $resourceType; + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $propName = null; + $rt = $this->resourceType; + if (!is_array($rt)) $rt = array($rt); + + foreach($rt as $resourceType) { + if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { + + if (isset($server->xmlNamespaces[$propName[1]])) { + $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2])); + } else { + $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2])); + } + + } + } + + } + + /** + * Returns the value in clark-notation + * + * For example '{DAV:}collection' + * + * @return string + */ + public function getValue() { + + return $this->resourceType; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php new file mode 100755 index 00000000..6c75e8df --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php @@ -0,0 +1,156 @@ +href = $href; + $this->responseProperties = $responseProperties; + + } + + /** + * Returns the url + * + * @return string + */ + public function getHref() { + + return $this->href; + + } + + /** + * Returns the property list + * + * @return array + */ + public function getResponseProperties() { + + return $this->responseProperties; + + } + + /** + * serialize + * + * @param Sabre_DAV_Server $server + * @param DOMElement $dom + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $dom) { + + $document = $dom->ownerDocument; + $properties = $this->responseProperties; + + $xresponse = $document->createElement('d:response'); + $dom->appendChild($xresponse); + + $uri = Sabre_DAV_URLUtil::encodePath($this->href); + + // Adding the baseurl to the beginning of the url + $uri = $server->getBaseUri() . $uri; + + $xresponse->appendChild($document->createElement('d:href',$uri)); + + // The properties variable is an array containing properties, grouped by + // HTTP status + foreach($properties as $httpStatus=>$propertyGroup) { + + // The 'href' is also in this array, and it's special cased. + // We will ignore it + if ($httpStatus=='href') continue; + + // If there are no properties in this group, we can also just carry on + if (!count($propertyGroup)) continue; + + $xpropstat = $document->createElement('d:propstat'); + $xresponse->appendChild($xpropstat); + + $xprop = $document->createElement('d:prop'); + $xpropstat->appendChild($xprop); + + $nsList = $server->xmlNamespaces; + + foreach($propertyGroup as $propertyName=>$propertyValue) { + + $propName = null; + preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName); + + // special case for empty namespaces + if ($propName[1]=='') { + + $currentProperty = $document->createElement($propName[2]); + $xprop->appendChild($currentProperty); + $currentProperty->setAttribute('xmlns',''); + + } else { + + if (!isset($nsList[$propName[1]])) { + $nsList[$propName[1]] = 'x' . count($nsList); + } + + // If the namespace was defined in the top-level xml namespaces, it means + // there was already a namespace declaration, and we don't have to worry about it. + if (isset($server->xmlNamespaces[$propName[1]])) { + $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]); + } else { + $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]); + } + $xprop->appendChild($currentProperty); + + } + + if (is_scalar($propertyValue)) { + $text = $document->createTextNode($propertyValue); + $currentProperty->appendChild($text); + } elseif ($propertyValue instanceof Sabre_DAV_Property) { + $propertyValue->serialize($server,$currentProperty); + } elseif (!is_null($propertyValue)) { + throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName); + } + + } + + $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus))); + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php new file mode 100755 index 00000000..0b7ca0cf --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php @@ -0,0 +1,76 @@ +supportsLocks = $supportsLocks; + + } + + /** + * serialize + * + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + $doc = $prop->ownerDocument; + + if (!$this->supportsLocks) return null; + + $lockEntry1 = $doc->createElementNS('DAV:','d:lockentry'); + $lockEntry2 = $doc->createElementNS('DAV:','d:lockentry'); + + $prop->appendChild($lockEntry1); + $prop->appendChild($lockEntry2); + + $lockScope1 = $doc->createElementNS('DAV:','d:lockscope'); + $lockScope2 = $doc->createElementNS('DAV:','d:lockscope'); + $lockType1 = $doc->createElementNS('DAV:','d:locktype'); + $lockType2 = $doc->createElementNS('DAV:','d:locktype'); + + $lockEntry1->appendChild($lockScope1); + $lockEntry1->appendChild($lockType1); + $lockEntry2->appendChild($lockScope2); + $lockEntry2->appendChild($lockType2); + + $lockScope1->appendChild($doc->createElementNS('DAV:','d:exclusive')); + $lockScope2->appendChild($doc->createElementNS('DAV:','d:shared')); + + $lockType1->appendChild($doc->createElementNS('DAV:','d:write')); + $lockType2->appendChild($doc->createElementNS('DAV:','d:write')); + + //$frag->appendXML(''); + //$frag->appendXML(''); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php new file mode 100755 index 00000000..8676f4c0 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php @@ -0,0 +1,110 @@ +addReport($reports); + + } + + /** + * Adds a report to this property + * + * The report must be a string in clark-notation. + * Multiple reports can be specified as an array. + * + * @param mixed $report + * @return void + */ + public function addReport($report) { + + if (!is_array($report)) $report = array($report); + + foreach($report as $r) { + + if (!preg_match('/^{([^}]*)}(.*)$/',$r)) + throw new Sabre_DAV_Exception('Reportname must be in clark-notation'); + + $this->reports[] = $r; + + } + + } + + /** + * Returns the list of supported reports + * + * @return array + */ + public function getValue() { + + return $this->reports; + + } + + /** + * Serializes the node + * + * @param Sabre_DAV_Server $server + * @param DOMElement $prop + * @return void + */ + public function serialize(Sabre_DAV_Server $server,DOMElement $prop) { + + foreach($this->reports as $reportName) { + + $supportedReport = $prop->ownerDocument->createElement('d:supported-report'); + $prop->appendChild($supportedReport); + + $report = $prop->ownerDocument->createElement('d:report'); + $supportedReport->appendChild($report); + + preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches); + + list(, $namespace, $element) = $matches; + + $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null; + + if ($prefix) { + $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element)); + } else { + $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element)); + } + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Server.php b/3.0/modules/webdav/libraries/Sabre/DAV/Server.php new file mode 100755 index 00000000..21a61256 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Server.php @@ -0,0 +1,1821 @@ + 'd', + 'http://sabredav.org/ns' => 's', + ); + + /** + * The propertymap can be used to map properties from + * requests to property classes. + * + * @var array + */ + public $propertyMap = array( + ); + + public $protectedProperties = array( + // RFC4918 + '{DAV:}getcontentlength', + '{DAV:}getetag', + '{DAV:}getlastmodified', + '{DAV:}lockdiscovery', + '{DAV:}resourcetype', + '{DAV:}supportedlock', + + // RFC4331 + '{DAV:}quota-available-bytes', + '{DAV:}quota-used-bytes', + + // RFC3744 + '{DAV:}alternate-URI-set', + '{DAV:}principal-URL', + '{DAV:}group-membership', + '{DAV:}supported-privilege-set', + '{DAV:}current-user-privilege-set', + '{DAV:}acl', + '{DAV:}acl-restrictions', + '{DAV:}inherited-acl-set', + '{DAV:}principal-collection-set', + + // RFC5397 + '{DAV:}current-user-principal', + ); + + /** + * This is a flag that allow or not showing file, line and code + * of the exception in the returned XML + * + * @var bool + */ + public $debugExceptions = false; + + + /** + * Sets up the server + * + * If a Sabre_DAV_Tree object is passed as an argument, it will + * use it as the directory tree. If a Sabre_DAV_INode is passed, it + * will create a Sabre_DAV_ObjectTree and use the node as the root. + * + * If nothing is passed, a Sabre_DAV_SimpleDirectory is created in + * a Sabre_DAV_ObjectTree. + * + * @param Sabre_DAV_Tree $tree The tree object + * @return void + */ + public function __construct($treeOrNode = null) { + + if ($treeOrNode instanceof Sabre_DAV_Tree) { + $this->tree = $treeOrNode; + } elseif ($treeOrNode instanceof Sabre_DAV_INode) { + $this->tree = new Sabre_DAV_ObjectTree($treeOrNode); + } elseif (is_null($treeOrNode)) { + $root = new Sabre_DAV_SimpleDirectory('root'); + $this->tree = new Sabre_DAV_ObjectTree($root); + } else { + throw new Sabre_DAV_Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre_DAV_Tree, Sabre_DAV_INode or null'); + } + $this->httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + /** + * Starts the DAV Server + * + * @return void + */ + public function exec() { + + try { + + $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri()); + + } catch (Exception $e) { + + $DOM = new DOMDocument('1.0','utf-8'); + $DOM->formatOutput = true; + + $error = $DOM->createElementNS('DAV:','d:error'); + $error->setAttribute('xmlns:s',self::NS_SABREDAV); + $DOM->appendChild($error); + + $error->appendChild($DOM->createElement('s:exception',get_class($e))); + $error->appendChild($DOM->createElement('s:message',$e->getMessage())); + if ($this->debugExceptions) { + $error->appendChild($DOM->createElement('s:file',$e->getFile())); + $error->appendChild($DOM->createElement('s:line',$e->getLine())); + $error->appendChild($DOM->createElement('s:code',$e->getCode())); + $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString())); + + } + $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION)); + + if($e instanceof Sabre_DAV_Exception) { + + $httpCode = $e->getHTTPCode(); + $e->serialize($this,$error); + $headers = $e->getHTTPHeaders($this); + + } else { + + $httpCode = 500; + $headers = array(); + + } + $headers['Content-Type'] = 'application/xml; charset=utf-8'; + + $this->httpResponse->sendStatus($httpCode); + $this->httpResponse->setHeaders($headers); + $this->httpResponse->sendBody($DOM->saveXML()); + + } + + } + + /** + * Sets the base server uri + * + * @param string $uri + * @return void + */ + public function setBaseUri($uri) { + + // If the baseUri does not end with a slash, we must add it + if ($uri[strlen($uri)-1]!=='/') + $uri.='/'; + + $this->baseUri = $uri; + + } + + /** + * Returns the base responding uri + * + * @return string + */ + public function getBaseUri() { + + if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri(); + return $this->baseUri; + + } + + /** + * This method attempts to detect the base uri. + * Only the PATH_INFO variable is considered. + * + * If this variable is not set, the root (/) is assumed. + * + * @return void + */ + public function guessBaseUri() { + + $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO'); + $uri = $this->httpRequest->getRawServerValue('REQUEST_URI'); + + // If PATH_INFO is not found, we just return / + if (!empty($pathInfo)) { + + // We need to make sure we ignore the QUERY_STRING part + if ($pos = strpos($uri,'?')) + $uri = substr($uri,0,$pos); + + // PATH_INFO is only set for urls, such as: /example.php/path + // in that case PATH_INFO contains '/path'. + // Note that REQUEST_URI is percent encoded, while PATH_INFO is + // not, Therefore they are only comparable if we first decode + // REQUEST_INFO as well. + $decodedUri = Sabre_DAV_URLUtil::decodePath($uri); + + // A simple sanity check: + if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) { + $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo)); + return rtrim($baseUri,'/') . '/'; + } + + throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); + + } + + // If the url ended with .php, we're going to assume that that's the server root + if (strpos($uri,'.php')===strlen($uri)-4) { + return $uri . '/'; + } + + // The last fallback is that we're just going to assume the server root. + return '/'; + + } + + /** + * Adds a plugin to the server + * + * For more information, console the documentation of Sabre_DAV_ServerPlugin + * + * @param Sabre_DAV_ServerPlugin $plugin + * @return void + */ + public function addPlugin(Sabre_DAV_ServerPlugin $plugin) { + + $this->plugins[get_class($plugin)] = $plugin; + $plugin->initialize($this); + + } + + /** + * Returns an initialized plugin by it's classname. + * + * This function returns null if the plugin was not found. + * + * @param string $className + * @return Sabre_DAV_ServerPlugin + */ + public function getPlugin($className) { + + if (isset($this->plugins[$className])) return $this->plugins[$className]; + return null; + + } + + /** + * Subscribe to an event. + * + * When the event is triggered, we'll call all the specified callbacks. + * It is possible to control the order of the callbacks through the + * priority argument. + * + * This is for example used to make sure that the authentication plugin + * is triggered before anything else. If it's not needed to change this + * number, it is recommended to ommit. + * + * @param string $event + * @param callback $callback + * @param int $priority + * @return void + */ + public function subscribeEvent($event, $callback, $priority = 100) { + + if (!isset($this->eventSubscriptions[$event])) { + $this->eventSubscriptions[$event] = array(); + } + while(isset($this->eventSubscriptions[$event][$priority])) $priority++; + $this->eventSubscriptions[$event][$priority] = $callback; + ksort($this->eventSubscriptions[$event]); + + } + + /** + * Broadcasts an event + * + * This method will call all subscribers. If one of the subscribers returns false, the process stops. + * + * The arguments parameter will be sent to all subscribers + * + * @param string $eventName + * @param array $arguments + * @return bool + */ + public function broadcastEvent($eventName,$arguments = array()) { + + if (isset($this->eventSubscriptions[$eventName])) { + + foreach($this->eventSubscriptions[$eventName] as $subscriber) { + + $result = call_user_func_array($subscriber,$arguments); + if ($result===false) return false; + + } + + } + + return true; + + } + + /** + * Handles a http request, and execute a method based on its name + * + * @param string $method + * @param string $uri + * @return void + */ + public function invokeMethod($method, $uri) { + + $method = strtoupper($method); + + if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return; + + // Make sure this is a HTTP method we support + $internalMethods = array( + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'MKCOL', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ); + + if (in_array($method,$internalMethods)) { + + call_user_func(array($this,'http' . $method), $uri); + + } else { + + if ($this->broadcastEvent('unknownMethod',array($method, $uri))) { + // Unsupported method + throw new Sabre_DAV_Exception_NotImplemented(); + } + + } + + } + + // {{{ HTTP Method implementations + + /** + * HTTP OPTIONS + * + * @param string $uri + * @return void + */ + protected function httpOptions($uri) { + + $methods = $this->getAllowedMethods($uri); + + $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods))); + $features = array('1','3', 'extended-mkcol'); + + foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures()); + + $this->httpResponse->setHeader('DAV',implode(', ',$features)); + $this->httpResponse->setHeader('MS-Author-Via','DAV'); + $this->httpResponse->setHeader('Accept-Ranges','bytes'); + $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION); + $this->httpResponse->setHeader('Content-Length',0); + $this->httpResponse->sendStatus(200); + + } + + /** + * HTTP GET + * + * This method simply fetches the contents of a uri, like normal + * + * @param string $uri + * @return void + */ + protected function httpGet($uri) { + + $node = $this->tree->getNodeForPath($uri,0); + + if (!$this->checkPreconditions(true)) return false; + + if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects'); + $body = $node->get(); + + // Converting string into stream, if needed. + if (is_string($body)) { + $stream = fopen('php://temp','r+'); + fwrite($stream,$body); + rewind($stream); + $body = $stream; + } + + /* + * TODO: getetag, getlastmodified, getsize should also be used using + * this method + */ + $httpHeaders = $this->getHTTPHeaders($uri); + + /* ContentType needs to get a default, because many webservers will otherwise + * default to text/html, and we don't want this for security reasons. + */ + if (!isset($httpHeaders['Content-Type'])) { + $httpHeaders['Content-Type'] = 'application/octet-stream'; + } + + + if (isset($httpHeaders['Content-Length'])) { + + $nodeSize = $httpHeaders['Content-Length']; + + // Need to unset Content-Length, because we'll handle that during figuring out the range + unset($httpHeaders['Content-Length']); + + } else { + $nodeSize = null; + } + + $this->httpResponse->setHeaders($httpHeaders); + + $range = $this->getHTTPRange(); + $ifRange = $this->httpRequest->getHeader('If-Range'); + $ignoreRangeHeader = false; + + // If ifRange is set, and range is specified, we first need to check + // the precondition. + if ($nodeSize && $range && $ifRange) { + + // if IfRange is parsable as a date we'll treat it as a DateTime + // otherwise, we must treat it as an etag. + try { + $ifRangeDate = new DateTime($ifRange); + + // It's a date. We must check if the entity is modified since + // the specified date. + if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true; + else { + $modified = new DateTime($httpHeaders['Last-Modified']); + if($modified > $ifRangeDate) $ignoreRangeHeader = true; + } + + } catch (Exception $e) { + + // It's an entity. We can do a simple comparison. + if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true; + elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true; + } + } + + // We're only going to support HTTP ranges if the backend provided a filesize + if (!$ignoreRangeHeader && $nodeSize && $range) { + + // Determining the exact byte offsets + if (!is_null($range[0])) { + + $start = $range[0]; + $end = $range[1]?$range[1]:$nodeSize-1; + if($start >= $nodeSize) + throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')'); + + if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')'); + if($end >= $nodeSize) $end = $nodeSize-1; + + } else { + + $start = $nodeSize-$range[1]; + $end = $nodeSize-1; + + if ($start<0) $start = 0; + + } + + // New read/write stream + $newStream = fopen('php://temp','r+'); + + stream_copy_to_stream($body, $newStream, $end-$start+1, $start); + rewind($newStream); + + $this->httpResponse->setHeader('Content-Length', $end-$start+1); + $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize); + $this->httpResponse->sendStatus(206); + $this->httpResponse->sendBody($newStream); + + + } else { + + if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize); + $this->httpResponse->sendStatus(200); + $this->httpResponse->sendBody($body); + + } + + } + + /** + * HTTP HEAD + * + * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body + * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again + * + * @param string $uri + * @return void + */ + protected function httpHead($uri) { + + $node = $this->tree->getNodeForPath($uri); + /* This information is only collection for File objects. + * Ideally we want to throw 405 Method Not Allowed for every + * non-file, but MS Office does not like this + */ + if ($node instanceof Sabre_DAV_IFile) { + $headers = $this->getHTTPHeaders($this->getRequestUri()); + if (!isset($headers['Content-Type'])) { + $headers['Content-Type'] = 'application/octet-stream'; + } + $this->httpResponse->setHeaders($headers); + } + $this->httpResponse->sendStatus(200); + + } + + /** + * HTTP Delete + * + * The HTTP delete method, deletes a given uri + * + * @param string $uri + * @return void + */ + protected function httpDelete($uri) { + + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; + $this->tree->delete($uri); + + $this->httpResponse->sendStatus(204); + $this->httpResponse->setHeader('Content-Length','0'); + + } + + + /** + * WebDAV PROPFIND + * + * This WebDAV method requests information about an uri resource, or a list of resources + * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value + * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory) + * + * The request body contains an XML data structure that has a list of properties the client understands + * The response body is also an xml document, containing information about every uri resource and the requested properties + * + * It has to return a HTTP 207 Multi-status status code + * + * @param string $uri + * @return void + */ + protected function httpPropfind($uri) { + + // $xml = new Sabre_DAV_XMLReader(file_get_contents('php://input')); + $requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true)); + + $depth = $this->getHTTPDepth(1); + // The only two options for the depth of a propfind is 0 or 1 + if ($depth!=0) $depth = 1; + + $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); + + // This is a multi-status response + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $data = $this->generateMultiStatus($newProperties); + $this->httpResponse->sendBody($data); + + } + + /** + * WebDAV PROPPATCH + * + * This method is called to update properties on a Node. The request is an XML body with all the mutations. + * In this XML body it is specified which properties should be set/updated and/or deleted + * + * @param string $uri + * @return void + */ + protected function httpPropPatch($uri) { + + $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true)); + + $result = $this->updateProperties($uri, $newProperties); + + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->httpResponse->sendBody( + $this->generateMultiStatus(array($result)) + ); + + } + + /** + * HTTP PUT method + * + * This HTTP method updates a file, or creates a new one. + * + * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 200 Ok + * + * @param string $uri + * @return void + */ + protected function httpPut($uri) { + + $body = $this->httpRequest->getBody(); + + // Intercepting the Finder problem + if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) { + + /** + Many webservers will not cooperate well with Finder PUT requests, + because it uses 'Chunked' transfer encoding for the request body. + + The symptom of this problem is that Finder sends files to the + server, but they arrive as 0-lenght files in PHP. + + If we don't do anything, the user might think they are uploading + files successfully, but they end up empty on the server. Instead, + we throw back an error if we detect this. + + The reason Finder uses Chunked, is because it thinks the files + might change as it's being uploaded, and therefore the + Content-Length can vary. + + Instead it sends the X-Expected-Entity-Length header with the size + of the file at the very start of the request. If this header is set, + but we don't get a request body we will fail the request to + protect the end-user. + */ + + // Only reading first byte + $firstByte = fread($body,1); + if (strlen($firstByte)!==1) { + throw new Sabre_DAV_Exception_Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.'); + } + + // The body needs to stay intact, so we copy everything to a + // temporary stream. + + $newBody = fopen('php://temp','r+'); + fwrite($newBody,$firstByte); + stream_copy_to_stream($body, $newBody); + rewind($newBody); + + $body = $newBody; + + } + + if ($this->tree->nodeExists($uri)) { + + $node = $this->tree->getNodeForPath($uri); + + // Checking If-None-Match and related headers. + if (!$this->checkPreconditions()) return; + + // If the node is a collection, we'll deny it + if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.'); + if (!$this->broadcastEvent('beforeWriteContent',array($this->getRequestUri()))) return false; + + $node->put($body); + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(200); + + } else { + + // If we got here, the resource didn't exist yet. + $this->createFile($this->getRequestUri(),$body); + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(201); + + } + + } + + + /** + * WebDAV MKCOL + * + * The MKCOL method is used to create a new collection (directory) on the server + * + * @param string $uri + * @return void + */ + protected function httpMkcol($uri) { + + $requestBody = $this->httpRequest->getBody(true); + + if ($requestBody) { + + $contentType = $this->httpRequest->getHeader('Content-Type'); + if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) { + + // We must throw 415 for unsupport mkcol bodies + throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type'); + + } + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody); + if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') { + + // We must throw 415 for unsupport mkcol bodies + throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.'); + + } + + $properties = array(); + foreach($dom->firstChild->childNodes as $childNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue; + $properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap)); + + } + if (!isset($properties['{DAV:}resourcetype'])) + throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property'); + + unset($properties['{DAV:}resourcetype']); + + $resourceType = array(); + // Need to parse out all the resourcetypes + $rtNode = $dom->firstChild->getElementsByTagNameNS('urn:DAV','resourcetype'); + $rtNode = $rtNode->item(0); + foreach($rtNode->childNodes as $childNode) {; + $resourceType[] = Sabre_DAV_XMLUtil::toClarkNotation($childNode); + } + + } else { + + $properties = array(); + $resourceType = array('{DAV:}collection'); + + } + + $result = $this->createCollection($uri, $resourceType, $properties); + + if (is_array($result)) { + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + + $this->httpResponse->sendBody( + $this->generateMultiStatus(array($result)) + ); + + } else { + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus(201); + } + + } + + /** + * WebDAV HTTP MOVE method + * + * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo + * + * @param string $uri + * @return void + */ + protected function httpMove($uri) { + + $moveInfo = $this->getCopyAndMoveInfo(); + if ($moveInfo['destinationExists']) { + + if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false; + $this->tree->delete($moveInfo['destination']); + + } + + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false; + if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false; + $this->tree->move($uri,$moveInfo['destination']); + $this->broadcastEvent('afterBind',array($moveInfo['destination'])); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201); + + } + + /** + * WebDAV HTTP COPY method + * + * This method copies one uri to a different uri, and works much like the MOVE request + * A lot of the actual request processing is done in getCopyMoveInfo + * + * @param string $uri + * @return void + */ + protected function httpCopy($uri) { + + $copyInfo = $this->getCopyAndMoveInfo(); + if ($copyInfo['destinationExists']) { + + if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false; + $this->tree->delete($copyInfo['destination']); + + } + if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false; + $this->tree->copy($uri,$copyInfo['destination']); + $this->broadcastEvent('afterBind',array($copyInfo['destination'])); + + // If a resource was overwritten we should send a 204, otherwise a 201 + $this->httpResponse->setHeader('Content-Length','0'); + $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201); + + } + + + + /** + * HTTP REPORT method implementation + * + * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253) + * It's used in a lot of extensions, so it made sense to implement it into the core. + * + * @param string $uri + * @return void + */ + protected function httpReport($uri) { + + $body = $this->httpRequest->getBody(true); + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $reportName = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild); + + if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) { + + // If broadcastEvent returned true, it means the report was not supported + throw new Sabre_DAV_Exception_ReportNotImplemented(); + + } + + } + + // }}} + // {{{ HTTP/WebDAV protocol helpers + + /** + * Returns an array with all the supported HTTP methods for a specific uri. + * + * @param string $uri + * @return array + */ + public function getAllowedMethods($uri) { + + $methods = array( + 'OPTIONS', + 'GET', + 'HEAD', + 'DELETE', + 'PROPFIND', + 'PUT', + 'PROPPATCH', + 'COPY', + 'MOVE', + 'REPORT' + ); + + // The MKCOL is only allowed on an unmapped uri + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + $methods[] = 'MKCOL'; + } + + // We're also checking if any of the plugins register any new methods + foreach($this->plugins as $plugin) $methods = array_merge($methods,$plugin->getHTTPMethods($uri)); + array_unique($methods); + + return $methods; + + } + + /** + * Gets the uri for the request, keeping the base uri into consideration + * + * @return string + */ + public function getRequestUri() { + + return $this->calculateUri($this->httpRequest->getUri()); + + } + + /** + * Calculates the uri for a request, making sure that the base uri is stripped out + * + * @param string $uri + * @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri + * @return string + */ + public function calculateUri($uri) { + + if ($uri[0]!='/' && strpos($uri,'://')) { + + $uri = parse_url($uri,PHP_URL_PATH); + + } + + $uri = str_replace('//','/',$uri); + + if (strpos($uri,$this->getBaseUri())===0) { + + return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/'); + + // A special case, if the baseUri was accessed without a trailing + // slash, we'll accept it as well. + } elseif ($uri.'/' === $this->getBaseUri()) { + + return ''; + + } else { + + throw new Sabre_DAV_Exception_Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')'); + + } + + } + + /** + * Returns the HTTP depth header + * + * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object + * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existant + * + * @param mixed $default + * @return int + */ + public function getHTTPDepth($default = self::DEPTH_INFINITY) { + + // If its not set, we'll grab the default + $depth = $this->httpRequest->getHeader('Depth'); + if (is_null($depth)) return $default; + + if ($depth == 'infinity') return self::DEPTH_INFINITY; + + // If its an unknown value. we'll grab the default + if (!ctype_digit($depth)) return $default; + + return (int)$depth; + + } + + /** + * Returns the HTTP range header + * + * This method returns null if there is no well-formed HTTP range request + * header or array($start, $end). + * + * The first number is the offset of the first byte in the range. + * The second number is the offset of the last byte in the range. + * + * If the second offset is null, it should be treated as the offset of the last byte of the entity + * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity + * + * return $mixed + */ + public function getHTTPRange() { + + $range = $this->httpRequest->getHeader('range'); + if (is_null($range)) return null; + + // Matching "Range: bytes=1234-5678: both numbers are optional + + if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null; + + if ($matches[1]==='' && $matches[2]==='') return null; + + return array( + $matches[1]!==''?$matches[1]:null, + $matches[2]!==''?$matches[2]:null, + ); + + } + + + /** + * Returns information about Copy and Move requests + * + * This function is created to help getting information about the source and the destination for the + * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions + * + * The returned value is an array with the following keys: + * * destination - Destination path + * * destinationExists - Wether or not the destination is an existing url (and should therefore be overwritten) + * + * @return array + */ + public function getCopyAndMoveInfo() { + + // Collecting the relevant HTTP headers + if (!$this->httpRequest->getHeader('Destination')) throw new Sabre_DAV_Exception_BadRequest('The destination header was not supplied'); + $destination = $this->calculateUri($this->httpRequest->getHeader('Destination')); + $overwrite = $this->httpRequest->getHeader('Overwrite'); + if (!$overwrite) $overwrite = 'T'; + if (strtoupper($overwrite)=='T') $overwrite = true; + elseif (strtoupper($overwrite)=='F') $overwrite = false; + // We need to throw a bad request exception, if the header was invalid + else throw new Sabre_DAV_Exception_BadRequest('The HTTP Overwrite header should be either T or F'); + + list($destinationDir) = Sabre_DAV_URLUtil::splitPath($destination); + + try { + $destinationParent = $this->tree->getNodeForPath($destinationDir); + if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection'); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + // If the destination parent node is not found, we throw a 409 + throw new Sabre_DAV_Exception_Conflict('The destination node is not found'); + } + + try { + + $destinationNode = $this->tree->getNodeForPath($destination); + + // If this succeeded, it means the destination already exists + // we'll need to throw precondition failed in case overwrite is false + if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite'); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + // Destination didn't exist, we're all good + $destinationNode = false; + + + + } + + // These are the three relevant properties we need to return + return array( + 'destination' => $destination, + 'destinationExists' => $destinationNode==true, + 'destinationNode' => $destinationNode, + ); + + } + + /** + * Returns a list of properties for a path + * + * This is a simplified version getPropertiesForPath. + * if you aren't interested in status codes, but you just + * want to have a flat list of properties. Use this method. + * + * @param string $path + * @param array $propertyNames + */ + public function getProperties($path, $propertyNames) { + + $result = $this->getPropertiesForPath($path,$propertyNames,0); + return $result[0][200]; + + } + + /** + * Returns a list of HTTP headers for a particular resource + * + * The generated http headers are based on properties provided by the + * resource. The method basically provides a simple mapping between + * DAV property and HTTP header. + * + * The headers are intended to be used for HEAD and GET requests. + * + * @param string $path + */ + public function getHTTPHeaders($path) { + + $propertyMap = array( + '{DAV:}getcontenttype' => 'Content-Type', + '{DAV:}getcontentlength' => 'Content-Length', + '{DAV:}getlastmodified' => 'Last-Modified', + '{DAV:}getetag' => 'ETag', + ); + + $properties = $this->getProperties($path,array_keys($propertyMap)); + + $headers = array(); + foreach($propertyMap as $property=>$header) { + if (!isset($properties[$property])) continue; + + if (is_scalar($properties[$property])) { + $headers[$header] = $properties[$property]; + + // GetLastModified gets special cased + } elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) { + $headers[$header] = $properties[$property]->getTime()->format(DateTime::RFC1123); + } + + } + + return $headers; + + } + + /** + * Returns a list of properties for a given path + * + * The path that should be supplied should have the baseUrl stripped out + * The list of properties should be supplied in Clark notation. If the list is empty + * 'allprops' is assumed. + * + * If a depth of 1 is requested child elements will also be returned. + * + * @param string $path + * @param array $propertyNames + * @param int $depth + * @return array + */ + public function getPropertiesForPath($path,$propertyNames = array(),$depth = 0) { + + if ($depth!=0) $depth = 1; + + $returnPropertyList = array(); + + $parentNode = $this->tree->getNodeForPath($path); + $nodes = array( + $path => $parentNode + ); + if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) { + foreach($this->tree->getChildren($path) as $childNode) + $nodes[$path . '/' . $childNode->getName()] = $childNode; + } + + // If the propertyNames array is empty, it means all properties are requested. + // We shouldn't actually return everything we know though, and only return a + // sensible list. + $allProperties = count($propertyNames)==0; + + foreach($nodes as $myPath=>$node) { + + $newProperties = array( + '200' => array(), + '404' => array(), + ); + if ($node instanceof Sabre_DAV_IProperties) + $newProperties['200'] = $node->getProperties($propertyNames); + + if ($allProperties) { + + // Default list of propertyNames, when all properties were requested. + $propertyNames = array( + '{DAV:}getlastmodified', + '{DAV:}getcontentlength', + '{DAV:}resourcetype', + '{DAV:}quota-used-bytes', + '{DAV:}quota-available-bytes', + '{DAV:}getetag', + '{DAV:}getcontenttype', + ); + + // We need to make sure this includes any propertyname already returned from + // $node->getProperties(); + $propertyNames = array_merge($propertyNames, array_keys($newProperties[200])); + + // Making sure there's no double entries + $propertyNames = array_unique($propertyNames); + + } + + // If the resourceType was not part of the list, we manually add it + // and mark it for removal. We need to know the resourcetype in order + // to make certain decisions about the entry. + // WebDAV dictates we should add a / and the end of href's for collections + $removeRT = false; + if (!in_array('{DAV:}resourcetype',$propertyNames)) { + $propertyNames[] = '{DAV:}resourcetype'; + $removeRT = true; + } + + foreach($propertyNames as $prop) { + + if (isset($newProperties[200][$prop])) continue; + + switch($prop) { + case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break; + case '{DAV:}getcontentlength' : if ($node instanceof Sabre_DAV_IFile) $newProperties[200][$prop] = (int)$node->getSize(); break; + case '{DAV:}resourcetype' : $newProperties[200][$prop] = new Sabre_DAV_Property_ResourceType($node instanceof Sabre_DAV_ICollection?self::NODE_DIRECTORY:self::NODE_FILE); break; + case '{DAV:}quota-used-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[0]; + } + break; + case '{DAV:}quota-available-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[1]; + } + break; + case '{DAV:}getetag' : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; + case '{DAV:}getcontenttype' : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; + case '{DAV:}supported-report-set' : $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet(); break; + + } + + // If we were unable to find the property, we will list it as 404. + if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; + + } + + $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties)); + + $newProperties['href'] = trim($myPath,'/'); + + // Its is a WebDAV recommendation to add a trailing slash to collectionnames. + // Apple's iCal also requires a trailing slash for principals (rfc 3744). + // Therefore we add a trailing / for any non-file. This might need adjustments + // if we find there are other edge cases. + if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && $newProperties[200]['{DAV:}resourcetype']->getValue()!==null) $newProperties['href'] .='/'; + + // If the resourcetype property was manually added to the requested property list, + // we will remove it again. + if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']); + + $returnPropertyList[] = $newProperties; + + } + + return $returnPropertyList; + + } + + /** + * This method is invoked by sub-systems creating a new file. + * + * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin). + * It was important to get this done through a centralized function, + * allowing plugins to intercept this using the beforeCreateFile event. + * + * @param string $uri + * @param resource $data + * @return void + */ + public function createFile($uri,$data) { + + list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri); + + if (!$this->broadcastEvent('beforeBind',array($uri))) return; + if (!$this->broadcastEvent('beforeCreateFile',array($uri,$data))) return; + + $parent = $this->tree->getNodeForPath($dir); + $parent->createFile($name,$data); + $this->tree->markDirty($dir); + + $this->broadcastEvent('afterBind',array($uri)); + } + + /** + * This method is invoked by sub-systems creating a new directory. + * + * @param string $uri + * @return void + */ + public function createDirectory($uri) { + + $this->createCollection($uri,array('{DAV:}collection'),array()); + + } + + /** + * Use this method to create a new collection + * + * The {DAV:}resourcetype is specified using the resourceType array. + * At the very least it must contain {DAV:}collection. + * + * The properties array can contain a list of additional properties. + * + * @param string $uri The new uri + * @param array $resourceType The resourceType(s) + * @param array $properties A list of properties + * @return void + */ + public function createCollection($uri, array $resourceType, array $properties) { + + list($parentUri,$newName) = Sabre_DAV_URLUtil::splitPath($uri); + + // Making sure {DAV:}collection was specified as resourceType + if (!in_array('{DAV:}collection', $resourceType)) { + throw new Sabre_DAV_Exception_InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection'); + } + + + // Making sure the parent exists + try { + + $parent = $this->tree->getNodeForPath($parentUri); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + throw new Sabre_DAV_Exception_Conflict('Parent node does not exist'); + + } + + // Making sure the parent is a collection + if (!$parent instanceof Sabre_DAV_ICollection) { + throw new Sabre_DAV_Exception_Conflict('Parent node is not a collection'); + } + + + + // Making sure the child does not already exist + try { + $parent->getChild($newName); + + // If we got here.. it means there's already a node on that url, and we need to throw a 405 + throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists'); + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + // This is correct + } + + + if (!$this->broadcastEvent('beforeBind',array($uri))) return; + + // There are 2 modes of operation. The standard collection + // creates the directory, and then updates properties + // the extended collection can create it directly. + if ($parent instanceof Sabre_DAV_IExtendedCollection) { + + $parent->createExtendedCollection($newName, $resourceType, $properties); + + } else { + + // No special resourcetypes are supported + if (count($resourceType)>1) { + throw new Sabre_DAV_Exception_InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.'); + } + + $parent->createDirectory($newName); + $rollBack = false; + $exception = null; + $errorResult = null; + + if (count($properties)>0) { + + try { + + $errorResult = $this->updateProperties($uri, $properties); + if (!isset($errorResult[200])) { + $rollBack = true; + } + + } catch (Sabre_DAV_Exception $e) { + + $rollBack = true; + $exception = $e; + + } + + } + + if ($rollBack) { + if (!$this->broadcastEvent('beforeUnbind',array($uri))) return; + $this->tree->delete($uri); + + // Re-throwing exception + if ($exception) throw $exception; + + return $errorResult; + } + + } + $this->tree->markDirty($parentUri); + $this->broadcastEvent('afterBind',array($uri)); + + } + + /** + * This method updates a resource's properties + * + * The properties array must be a list of properties. Array-keys are + * property names in clarknotation, array-values are it's values. + * If a property must be deleted, the value should be null. + * + * Note that this request should either completely succeed, or + * completely fail. + * + * The response is an array with statuscodes for keys, which in turn + * contain arrays with propertynames. This response can be used + * to generate a multistatus body. + * + * @param string $uri + * @param array $properties + * @return array + */ + public function updateProperties($uri, array $properties) { + + // we'll start by grabbing the node, this will throw the appropriate + // exceptions if it doesn't. + $node = $this->tree->getNodeForPath($uri); + + $result = array( + 200 => array(), + 403 => array(), + 424 => array(), + ); + $remainingProperties = $properties; + $hasError = false; + + + // If the node is not an instance of Sabre_DAV_IProperties, every + // property is 403 Forbidden + // simply return a 405. + if (!($node instanceof Sabre_DAV_IProperties)) { + $hasError = true; + foreach($properties as $propertyName=> $value) { + $result[403][$propertyName] = null; + } + $remainingProperties = array(); + } + + // Running through all properties to make sure none of them are protected + if (!$hasError) foreach($properties as $propertyName => $value) { + if(in_array($propertyName, $this->protectedProperties)) { + $result[403][$propertyName] = null; + unset($remainingProperties[$propertyName]); + $hasError = true; + } + } + + // Only if there were no errors we may attempt to update the resource + if (!$hasError) { + $updateResult = $node->updateProperties($properties); + $remainingProperties = array(); + + if ($updateResult===true) { + // success + foreach($properties as $propertyName=>$value) { + $result[200][$propertyName] = null; + } + + } elseif ($updateResult===false) { + // The node failed to update the properties for an + // unknown reason + foreach($properties as $propertyName=>$value) { + $result[403][$propertyName] = null; + } + + } elseif (is_array($updateResult)) { + // The node has detailed update information + $result = $updateResult; + + } else { + throw new Sabre_DAV_Exception('Invalid result from updateProperties'); + } + + } + + foreach($remainingProperties as $propertyName=>$value) { + // if there are remaining properties, it must mean + // there's a dependency failure + $result[424][$propertyName] = null; + } + + // Removing empty array values + foreach($result as $status=>$props) { + + if (count($props)===0) unset($result[$status]); + + } + $result['href'] = $uri; + return $result; + + } + + /** + * This method checks the main HTTP preconditions. + * + * Currently these are: + * * If-Match + * * If-None-Match + * * If-Modified-Since + * * If-Unmodified-Since + * + * The method will return true if all preconditions are met + * The method will return false, or throw an exception if preconditions + * failed. If false is returned the operation should be aborted, and + * the appropriate HTTP response headers are already set. + * + * Normally this method will throw 412 Precondition Failed for failures + * related to If-None-Match, If-Match and If-Unmodified Since. It will + * set the status to 304 Not Modified for If-Modified_since. + * + * If the $handleAsGET argument is set to true, it will also return 304 + * Not Modified for failure of the If-None-Match precondition. This is the + * desired behaviour for HTTP GET and HTTP HEAD requests. + * + * @return bool + */ + public function checkPreconditions($handleAsGET = false) { + + $uri = $this->getRequestUri(); + $node = null; + $lastMod = null; + $etag = null; + + if ($ifMatch = $this->httpRequest->getHeader('If-Match')) { + + // If-Match contains an entity tag. Only if the entity-tag + // matches we are allowed to make the request succeed. + // If the entity-tag is '*' we are only allowed to make the + // request succeed if a resource exists at that url. + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match'); + } + + // Only need to check entity tags if they are not * + if ($ifMatch!=='*') { + + // There can be multiple etags + $ifMatch = explode(',',$ifMatch); + $haveMatch = false; + foreach($ifMatch as $ifMatchItem) { + + // Stripping any extra spaces + $ifMatchItem = trim($ifMatchItem,' '); + + $etag = $node->getETag(); + if ($etag===$ifMatchItem) { + $haveMatch = true; + } + } + if (!$haveMatch) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match'); + } + } + } + + if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) { + + // The If-None-Match header contains an etag. + // Only if the ETag does not match the current ETag, the request will succeed + // The header can also contain *, in which case the request + // will only succeed if the entity does not exist at all. + $nodeExists = true; + if (!$node) { + try { + $node = $this->tree->getNodeForPath($uri); + } catch (Sabre_DAV_Exception_FileNotFound $e) { + $nodeExists = false; + } + } + if ($nodeExists) { + $haveMatch = false; + if ($ifNoneMatch==='*') $haveMatch = true; + else { + + // There might be multiple etags + $ifNoneMatch = explode(',', $ifNoneMatch); + $etag = $node->getETag(); + + foreach($ifNoneMatch as $ifNoneMatchItem) { + + // Stripping any extra spaces + $ifNoneMatchItem = trim($ifNoneMatchItem,' '); + + if ($etag===$ifNoneMatchItem) $haveMatch = true; + + } + + } + + if ($haveMatch) { + if ($handleAsGET) { + $this->httpResponse->sendStatus(304); + return false; + } else { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match'); + } + } + } + + } + + if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) { + + // The If-Modified-Since header contains a date. We + // will only return the entity if it has been changed since + // that date. If it hasn't been changed, we return a 304 + // header + // Note that this header only has to be checked if there was no If-None-Match header + // as per the HTTP spec. + $date = Sabre_HTTP_Util::parseHTTPDate($ifModifiedSince); + + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($uri); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new DateTime('@' . $lastMod); + if ($lastMod <= $date) { + $this->httpResponse->sendStatus(304); + return false; + } + } + } + } + + if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) { + + // The If-Unmodified-Since will allow allow the request if the + // entity has not changed since the specified date. + $date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince); + + // We must only check the date if it's valid + if ($date) { + if (is_null($node)) { + $node = $this->tree->getNodeForPath($uri); + } + $lastMod = $node->getLastModified(); + if ($lastMod) { + $lastMod = new DateTime('@' . $lastMod); + if ($lastMod > $date) { + throw new Sabre_DAV_Exception_PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since'); + } + } + } + + } + return true; + + } + + // }}} + // {{{ XML Readers & Writers + + + /** + * Generates a WebDAV propfind response body based on a list of nodes + * + * @param array $fileProperties The list with nodes + * @param array $requestedProperties The properties that should be returned + * @return string + */ + public function generateMultiStatus(array $fileProperties) { + + $dom = new DOMDocument('1.0','utf-8'); + //$dom->formatOutput = true; + $multiStatus = $dom->createElement('d:multistatus'); + $dom->appendChild($multiStatus); + + // Adding in default namespaces + foreach($this->xmlNamespaces as $namespace=>$prefix) { + + $multiStatus->setAttribute('xmlns:' . $prefix,$namespace); + + } + + foreach($fileProperties as $entry) { + + $href = $entry['href']; + unset($entry['href']); + + $response = new Sabre_DAV_Property_Response($href,$entry); + $response->serialize($this,$multiStatus); + + } + + return $dom->saveXML(); + + } + + /** + * This method parses a PropPatch request + * + * PropPatch changes the properties for a resource. This method + * returns a list of properties. + * + * The keys in the returned array contain the property name (e.g.: {DAV:}displayname, + * and the value contains the property value. If a property is to be removed the value + * will be null. + * + * @param string $body xml body + * @return array list of properties in need of updating or deletion + */ + public function parsePropPatchRequest($body) { + + //We'll need to change the DAV namespace declaration to something else in order to make it parsable + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + + $newProperties = array(); + + foreach($dom->firstChild->childNodes as $child) { + + if ($child->nodeType !== XML_ELEMENT_NODE) continue; + + $operation = Sabre_DAV_XMLUtil::toClarkNotation($child); + + if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue; + + $innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap); + + foreach($innerProperties as $propertyName=>$propertyValue) { + + if ($operation==='{DAV:}remove') { + $propertyValue = null; + } + + $newProperties[$propertyName] = $propertyValue; + + } + + } + + return $newProperties; + + } + + /** + * This method parses the PROPFIND request and returns its information + * + * This will either be a list of properties, or an empty array; in which case + * an {DAV:}allprop was requested. + * + * @param string $body + * @return array + */ + public function parsePropFindRequest($body) { + + // If the propfind body was empty, it means IE is requesting 'all' properties + if (!$body) return array(); + + $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); + $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0); + return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); + + } + + // }}} + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php new file mode 100755 index 00000000..f38e8aa3 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php @@ -0,0 +1,60 @@ +name = $name; + foreach($children as $child) { + + if (!($child instanceof Sabre_DAV_INode)) throw new Sabre_DAV_Exception('Only instances of Sabre_DAV_INode are allowed to be passed in the children argument'); + $this->addChild($child); + + } + + } + + /** + * Adds a new childnode to this collection + * + * @param Sabre_DAV_INode $child + * @return void + */ + public function addChild(Sabre_DAV_INode $child) { + + $this->children[$child->getName()] = $child; + + } + + /** + * Returns the name of the collection + * + * @return string + */ + public function getName() { + + return $this->name; + + } + + /** + * Returns a child object, by its name. + * + * This method makes use of the getChildren method to grab all the child nodes, and compares the name. + * Generally its wise to override this, as this can usually be optimized + * + * @param string $name + * @throws Sabre_DAV_Exception_FileNotFound + * @return Sabre_DAV_INode + */ + public function getChild($name) { + + if (isset($this->children[$name])) return $this->children[$name]; + throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name); + + } + + /** + * Returns a list of children for this collection + * + * @return array + */ + public function getChildren() { + + return array_values($this->children); + + } + + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php b/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php new file mode 100755 index 00000000..1e15865e --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php @@ -0,0 +1,275 @@ +dataDir = $dataDir; + + } + + /** + * Initialize the plugin + * + * This is called automatically be the Server class after this plugin is + * added with Sabre_DAV_Server::addPlugin() + * + * @param Sabre_DAV_Server $server + * @return void + */ + public function initialize(Sabre_DAV_Server $server) { + $this->server = $server; + $server->subscribeEvent('beforeMethod',array($this,'beforeMethod')); + $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile')); + + } + + /** + * This method is called before any HTTP method handler + * + * This method intercepts any GET, DELETE, PUT and PROPFIND calls to + * filenames that are known to match the 'temporary file' regex. + * + * @param string $method + * @return bool + */ + public function beforeMethod($method, $uri) { + + if (!$tempLocation = $this->isTempFile($uri)) + return true; + + switch($method) { + case 'GET' : + return $this->httpGet($tempLocation); + case 'PUT' : + return $this->httpPut($tempLocation); + case 'PROPFIND' : + return $this->httpPropfind($tempLocation, $uri); + case 'DELETE' : + return $this->httpDelete($tempLocation); + } + return true; + + } + + /** + * This method is invoked if some subsystem creates a new file. + * + * This is used to deal with HTTP LOCK requests which create a new + * file. + * + * @param string $uri + * @param resource $data + * @return bool + */ + public function beforeCreateFile($uri,$data) { + if ($tempPath = $this->isTempFile($uri)) { + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + file_put_contents($tempPath,$data); + return false; + } + return true; + + } + + /** + * This method will check if the url matches the temporary file pattern + * if it does, it will return an path based on $this->dataDir for the + * temporary file storage. + * + * @param string $path + * @return boolean|string + */ + protected function isTempFile($path) { + + // We're only interested in the basename. + list(, $tempPath) = Sabre_DAV_URLUtil::splitPath($path); + + foreach($this->temporaryFilePatterns as $tempFile) { + + if (preg_match($tempFile,$tempPath)) { + return $this->dataDir . '/sabredav_' . md5($path) . '.tempfile'; + } + + } + + return false; + + } + + + /** + * This method handles the GET method for temporary files. + * If the file doesn't exist, it will return false which will kick in + * the regular system for the GET method. + * + * @param string $tempLocation + * @return bool + */ + public function httpGet($tempLocation) { + + if (!file_exists($tempLocation)) return true; + + $hR = $this->server->httpResponse; + $hR->setHeader('Content-Type','application/octet-stream'); + $hR->setHeader('Content-Length',filesize($tempLocation)); + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(200); + $hR->sendBody(fopen($tempLocation,'r')); + return false; + + } + + /** + * This method handles the PUT method. + * + * @param string $tempLocation + * @return bool + */ + public function httpPut($tempLocation) { + error_log("Tempfile PUT"); + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + + $newFile = !file_exists($tempLocation); + + if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) { + throw new Sabre_DAV_Exception_PreconditionFailed('The resource already exists, and an If-None-Match header was supplied'); + } + + file_put_contents($tempLocation,$this->server->httpRequest->getBody()); + $hR->sendStatus($newFile?201:200); + return false; + + } + + /** + * This method handles the DELETE method. + * + * If the file didn't exist, it will return false, which will make the + * standard HTTP DELETE handler kick in. + * + * @param string $tempLocation + * @return bool + */ + public function httpDelete($tempLocation) { + + if (!file_exists($tempLocation)) return true; + + unlink($tempLocation); + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(204); + return false; + + } + + /** + * This method handles the PROPFIND method. + * + * It's a very lazy method, it won't bother checking the request body + * for which properties were requested, and just sends back a default + * set of properties. + * + * @param string $tempLocation + * @return void + */ + public function httpPropfind($tempLocation, $uri) { + + if (!file_exists($tempLocation)) return true; + + $hR = $this->server->httpResponse; + $hR->setHeader('X-Sabre-Temp','true'); + $hR->sendStatus(207); + $hR->setHeader('Content-Type','application/xml; charset=utf-8'); + + $requestedProps = $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); + + $properties = array( + 'href' => $uri, + 200 => array( + '{DAV:}getlastmodified' => new Sabre_DAV_Property_GetLastModified(filemtime($tempLocation)), + '{DAV:}getcontentlength' => filesize($tempLocation), + '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType(null), + '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true, + + ), + ); + + $data = $this->server->generateMultiStatus(array($properties)); + $hR->sendBody($data); + return false; + + } + + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php b/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php new file mode 100755 index 00000000..f7191e23 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php @@ -0,0 +1,192 @@ +getNodeForPath($path); + return true; + + } catch (Sabre_DAV_Exception_FileNotFound $e) { + + return false; + + } + + } + + /** + * Copies a file from path to another + * + * @param string $sourcePath The source location + * @param string $destinationPath The full destination path + * @return void + */ + public function copy($sourcePath, $destinationPath) { + + $sourceNode = $this->getNodeForPath($sourcePath); + + // grab the dirname and basename components + list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); + + $destinationParent = $this->getNodeForPath($destinationDir); + $this->copyNode($sourceNode,$destinationParent,$destinationName); + + $this->markDirty($destinationDir); + + } + + /** + * Moves a file from one location to another + * + * @param string $sourcePath The path to the file which should be moved + * @param string $destinationPath The full destination path, so not just the destination parent node + * @return int + */ + public function move($sourcePath, $destinationPath) { + + list($sourceDir, $sourceName) = Sabre_DAV_URLUtil::splitPath($sourcePath); + list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath); + + if ($sourceDir===$destinationDir) { + $renameable = $this->getNodeForPath($sourcePath); + $renameable->setName($destinationName); + } else { + $this->copy($sourcePath,$destinationPath); + $this->getNodeForPath($sourcePath)->delete(); + } + $this->markDirty($sourceDir); + $this->markDirty($destinationDir); + + } + + /** + * Deletes a node from the tree + * + * @param string $path + * @return void + */ + public function delete($path) { + + $node = $this->getNodeForPath($path); + $node->delete(); + + list($parent) = Sabre_DAV_URLUtil::splitPath($path); + $this->markDirty($parent); + + } + + /** + * Returns a list of childnodes for a given path. + * + * @param string $path + * @return array + */ + public function getChildren($path) { + + $node = $this->getNodeForPath($path); + return $node->getChildren(); + + } + + /** + * This method is called with every tree update + * + * Examples of tree updates are: + * * node deletions + * * node creations + * * copy + * * move + * * renaming nodes + * + * If Tree classes implement a form of caching, this will allow + * them to make sure caches will be expired. + * + * If a path is passed, it is assumed that the entire subtree is dirty + * + * @param string $path + * @return void + */ + public function markDirty($path) { + + + } + + /** + * copyNode + * + * @param Sabre_DAV_INode $source + * @param Sabre_DAV_ICollection $destination + * @return void + */ + protected function copyNode(Sabre_DAV_INode $source,Sabre_DAV_ICollection $destinationParent,$destinationName = null) { + + if (!$destinationName) $destinationName = $source->getName(); + + if ($source instanceof Sabre_DAV_IFile) { + + $data = $source->get(); + + // If the body was a string, we need to convert it to a stream + if (is_string($data)) { + $stream = fopen('php://temp','r+'); + fwrite($stream,$data); + rewind($stream); + $data = $stream; + } + $destinationParent->createFile($destinationName,$data); + $destination = $destinationParent->getChild($destinationName); + + } elseif ($source instanceof Sabre_DAV_ICollection) { + + $destinationParent->createDirectory($destinationName); + + $destination = $destinationParent->getChild($destinationName); + foreach($source->getChildren() as $child) { + + $this->copyNode($child,$destination); + + } + + } + if ($source instanceof Sabre_DAV_IProperties && $destination instanceof Sabre_DAV_IProperties) { + + $props = $source->getProperties(array()); + $destination->updateProperties($props); + + } + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php b/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php new file mode 100755 index 00000000..ee195eb2 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php @@ -0,0 +1,124 @@ +basePath = $basePath; + + } + + /** + * Returns a new node for the given path + * + * @param string $path + * @return void + */ + public function getNodeForPath($path) { + + $realPath = $this->getRealPath($path); + if (!file_exists($realPath)) throw new Sabre_DAV_Exception_FileNotFound('File at location ' . $realPath . ' not found'); + if (is_dir($realPath)) { + return new Sabre_DAV_FS_Directory($path); + } else { + return new Sabre_DAV_FS_File($path); + } + + } + + /** + * Returns the real filesystem path for a webdav url. + * + * @param string $publicPath + * @return string + */ + protected function getRealPath($publicPath) { + + return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/'); + + } + + /** + * Copies a file or directory. + * + * This method must work recursively and delete the destination + * if it exists + * + * @param string $source + * @param string $destination + * @return void + */ + public function copy($source,$destination) { + + $source = $this->getRealPath($source); + $destination = $this->getRealPath($destination); + $this->realCopy($source,$destination); + + } + + /** + * Used by self::copy + * + * @param string $source + * @param string $destination + * @return void + */ + protected function realCopy($source,$destination) { + + if (is_file($source)) { + copy($source,$destination); + } else { + mkdir($destination); + foreach(scandir($source) as $subnode) { + + if ($subnode=='.' || $subnode=='..') continue; + $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode); + + } + } + + } + + /** + * Moves a file or directory recursively. + * + * If the destination exists, delete it first. + * + * @param string $source + * @param string $destination + * @return void + */ + public function move($source,$destination) { + + $source = $this->getRealPath($source); + $destination = $this->getRealPath($destination); + rename($source,$destination); + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php b/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php new file mode 100755 index 00000000..0dfc6896 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php @@ -0,0 +1,141 @@ +=0x41 /* A */ && $c<=0x5a /* Z */) || + ($c>=0x61 /* a */ && $c<=0x7a /* z */) || + ($c>=0x30 /* 0 */ && $c<=0x39 /* 9 */) || + $c===0x5f /* _ */ || + $c===0x2d /* - */ || + $c===0x2e /* . */ || + $c===0x7E /* ~ */ || + + /* Reserved, but no reserved purpose */ + $c===0x28 /* ( */ || + $c===0x29 /* ) */ + + ) { + $newStr.=$pathSegment[$i]; + } else { + $newStr.='%' . str_pad(dechex($c), 2, '0', STR_PAD_LEFT); + } + + } + return $newStr; + + } + + /** + * Decodes a url-encoded path + * + * @param string $path + * @return string + */ + static function decodePath($path) { + + return self::decodePathSegment($path); + + } + + /** + * Decodes a url-encoded path segment + * + * @param string $path + * @return string + */ + static function decodePathSegment($path) { + + $path = urldecode($path); + $encoding = mb_detect_encoding($path, array('UTF-8','ISO-8859-1')); + + switch($encoding) { + + case 'ISO-8859-1' : + $path = utf8_encode($path); + } + + return $path; + + } + + /** + * Returns the 'dirname' and 'basename' for a path. + * + * The reason there is a custom function for this purpose, is because + * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale is used) + * and we need a method that just operates on UTF-8 characters. + * + * In addition basename and dirname are platform aware, and will treat backslash (\) as a + * directory separator on windows. + * + * This method returns the 2 components as an array. + * + * If there is no dirname, it will return an empty string. Any / appearing at the end of the + * string is stripped off. + * + * @param string $path + * @return array + */ + static function splitPath($path) { + + $matches = array(); + if(preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u',$path,$matches)) { + return array($matches[1],$matches[2]); + } else { + return array(null,null); + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Version.php b/3.0/modules/webdav/libraries/Sabre/DAV/Version.php new file mode 100755 index 00000000..20584782 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/DAV/Version.php @@ -0,0 +1,24 @@ + + * will be returned as: + * {http://www.example.org}myelem + * + * This format is used throughout the SabreDAV sourcecode. + * Elements encoded with the urn:DAV namespace will + * be returned as if they were in the DAV: namespace. This is to avoid + * compatibility problems. + * + * This function will return null if a nodetype other than an Element is passed. + * + * @param DOMElement $dom + * @return string + */ + static function toClarkNotation(DOMNode $dom) { + + if ($dom->nodeType !== XML_ELEMENT_NODE) return null; + + // Mapping back to the real namespace, in case it was dav + if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI; + + // Mapping to clark notation + return '{' . $ns . '}' . $dom->localName; + + } + + /** + * This method takes an XML document (as string) and converts all instances of the + * DAV: namespace to urn:DAV + * + * This is unfortunately needed, because the DAV: namespace violates the xml namespaces + * spec, and causes the DOM to throw errors + */ + static function convertDAVNamespace($xmlDocument) { + + // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV: + // namespace is actually a violation of the XML namespaces specification, and will cause errors + return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument); + + } + + /** + * This method provides a generic way to load a DOMDocument for WebDAV use. + * + * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors. + * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. + * + * @param string $xml + * @throws Sabre_DAV_Exception_BadRequest + * @return DOMDocument + */ + static function loadDOMDocument($xml) { + + if (empty($xml)) + throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent'); + + // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower) + // does not support this, so we must intercept this and convert to UTF-8. + if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") { + + // Note: the preceeding byte sequence is "]*)encoding="UTF-16"([^>]*)>|u','',$xml); + + } + + // Retaining old error setting + $oldErrorSetting = libxml_use_internal_errors(true); + + // Clearing any previous errors + libxml_clear_errors(); + + $dom = new DOMDocument(); + $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR); + + // We don't generally care about any whitespace + $dom->preserveWhiteSpace = false; + + if ($error = libxml_get_last_error()) { + libxml_clear_errors(); + throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')'); + } + + // Restoring old mechanism for error handling + if ($oldErrorSetting===false) libxml_use_internal_errors(false); + + return $dom; + + } + + /** + * Parses all WebDAV properties out of a DOM Element + * + * Generally WebDAV properties are encloded in {DAV:}prop elements. This + * method helps by going through all these and pulling out the actual + * propertynames, making them array keys and making the property values, + * well.. the array values. + * + * If no value was given (self-closing element) null will be used as the + * value. This is used in for example PROPFIND requests. + * + * Complex values are supported through the propertyMap argument. The + * propertyMap should have the clark-notation properties as it's keys, and + * classnames as values. + * + * When any of these properties are found, the unserialize() method will be + * (statically) called. The result of this method is used as the value. + * + * @param DOMElement $parentNode + * @param array $propertyMap + * @return array + */ + static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) { + + $propList = array(); + foreach($parentNode->childNodes as $propNode) { + + if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue; + + foreach($propNode->childNodes as $propNodeData) { + + /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */ + if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue; + + $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData); + if (isset($propertyMap[$propertyName])) { + $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); + } else { + $propList[$propertyName] = $propNodeData->textContent; + } + } + + + } + return $propList; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php new file mode 100755 index 00000000..b97fea9d --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php @@ -0,0 +1,226 @@ +httpRequest->getHeader('Authorization'); + $authHeader = explode(' ',$authHeader); + + if ($authHeader[0]!='AWS' || !isset($authHeader[1])) { + $this->errorCode = self::ERR_NOAWSHEADER; + return false; + } + + list($this->accessKey,$this->signature) = explode(':',$authHeader[1]); + + return true; + + } + + /** + * Returns the username for the request + * + * @return string + */ + public function getAccessKey() { + + return $this->accessKey; + + } + + /** + * Validates the signature based on the secretKey + * + * @return bool + */ + public function validate($secretKey) { + + $contentMD5 = $this->httpRequest->getHeader('Content-MD5'); + + if ($contentMD5) { + // We need to validate the integrity of the request + $body = $this->httpRequest->getBody(true); + $this->httpRequest->setBody($body,true); + + if ($contentMD5!=base64_encode(md5($body,true))) { + // content-md5 header did not match md5 signature of body + $this->errorCode = self::ERR_MD5CHECKSUMWRONG; + return false; + } + + } + + if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) + $requestDate = $this->httpRequest->getHeader('Date'); + + if (!$this->validateRFC2616Date($requestDate)) + return false; + + $amzHeaders = $this->getAmzHeaders(); + + $signature = base64_encode( + $this->hmacsha1($secretKey, + $this->httpRequest->getMethod() . "\n" . + $contentMD5 . "\n" . + $this->httpRequest->getHeader('Content-type') . "\n" . + $requestDate . "\n" . + $amzHeaders . + $this->httpRequest->getURI() + ) + ); + + if ($this->signature != $signature) { + + $this->errorCode = self::ERR_INVALIDSIGNATURE; + return false; + + } + + return true; + + } + + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','AWS'); + $this->httpResponse->sendStatus(401); + + } + + /** + * Makes sure the supplied value is a valid RFC2616 date. + * + * If we would just use strtotime to get a valid timestamp, we have no way of checking if a + * user just supplied the word 'now' for the date header. + * + * This function also makes sure the Date header is within 15 minutes of the operating + * system date, to prevent replay attacks. + * + * @param string $dateHeader + * @return bool + */ + protected function validateRFC2616Date($dateHeader) { + + $date = Sabre_HTTP_Util::parseHTTPDate($dateHeader); + + // Unknown format + if (!$date) { + $this->errorCode = self::ERR_INVALIDDATEFORMAT; + return false; + } + + $min = new DateTime('-15 minutes'); + $max = new DateTime('+15 minutes'); + + // We allow 15 minutes around the current date/time + if ($date > $max || $date < $min) { + $this->errorCode = self::ERR_REQUESTTIMESKEWED; + return false; + } + + return $date; + + } + + /** + * Returns a list of AMZ headers + * + * @return void + */ + protected function getAmzHeaders() { + + $amzHeaders = array(); + $headers = $this->httpRequest->getHeaders(); + foreach($headers as $headerName => $headerValue) { + if (strpos(strtolower($headerName),'x-amz-')===0) { + $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n"; + } + } + ksort($amzHeaders); + + $headerStr = ''; + foreach($amzHeaders as $h=>$v) { + $headerStr.=$h.':'.$v; + } + + return $headerStr; + + } + + /** + * Generates an HMAC-SHA1 signature + * + * @param string $key + * @param string $message + * @return string + */ + private function hmacsha1($key, $message) { + + $blocksize=64; + if (strlen($key)>$blocksize) + $key=pack('H*', sha1($key)); + $key=str_pad($key,$blocksize,chr(0x00)); + $ipad=str_repeat(chr(0x36),$blocksize); + $opad=str_repeat(chr(0x5c),$blocksize); + $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message)))); + return $hmac; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php new file mode 100755 index 00000000..0abc4bcb --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php @@ -0,0 +1,111 @@ +httpResponse = new Sabre_HTTP_Response(); + $this->httpRequest = new Sabre_HTTP_Request(); + + } + + /** + * Sets an alternative HTTP response object + * + * @param Sabre_HTTP_Response $response + * @return void + */ + public function setHTTPResponse(Sabre_HTTP_Response $response) { + + $this->httpResponse = $response; + + } + + /** + * Sets an alternative HTTP request object + * + * @param Sabre_HTTP_Request $request + * @return void + */ + public function setHTTPRequest(Sabre_HTTP_Request $request) { + + $this->httpRequest = $request; + + } + + + /** + * Sets the realm + * + * The realm is often displayed in authentication dialog boxes + * Commonly an application name displayed here + * + * @param string $realm + * @return void + */ + public function setRealm($realm) { + + $this->realm = $realm; + + } + + /** + * Returns the realm + * + * @return string + */ + public function getRealm() { + + return $this->realm; + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + abstract public function requireLogin(); + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php new file mode 100755 index 00000000..c0231b4e --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php @@ -0,0 +1,61 @@ +httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) { + + return array($user,$pass); + + } + + // Most other webservers + $auth = $this->httpRequest->getHeader('Authorization'); + + if (!$auth) return false; + + if (strpos(strtolower($auth),'basic')!==0) return false; + + return explode(':', base64_decode(substr($auth, 6))); + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"'); + $this->httpResponse->sendStatus(401); + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php b/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php new file mode 100755 index 00000000..bce59594 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php @@ -0,0 +1,234 @@ +nonce = uniqid(); + $this->opaque = md5($this->realm); + parent::__construct(); + + } + + /** + * Gathers all information from the headers + * + * This method needs to be called prior to anything else. + * + * @return void + */ + public function init() { + + $digest = $this->getDigest(); + $this->digestParts = $this->parseDigest($digest); + + } + + /** + * Sets the quality of protection value. + * + * Possible values are: + * Sabre_HTTP_DigestAuth::QOP_AUTH + * Sabre_HTTP_DigestAuth::QOP_AUTHINT + * + * Multiple values can be specified using logical OR. + * + * QOP_AUTHINT ensures integrity of the request body, but this is not + * supported by most HTTP clients. QOP_AUTHINT also requires the entire + * request body to be md5'ed, which can put strains on CPU and memory. + * + * @param int $qop + * @return void + */ + public function setQOP($qop) { + + $this->qop = $qop; + + } + + /** + * Validates the user. + * + * The A1 parameter should be md5($username . ':' . $realm . ':' . $password); + * + * @param string $A1 + * @return bool + */ + public function validateA1($A1) { + + $this->A1 = $A1; + return $this->validate(); + + } + + /** + * Validates authentication through a password. The actual password must be provided here. + * It is strongly recommended not store the password in plain-text and use validateA1 instead. + * + * @param string $password + * @return bool + */ + public function validatePassword($password) { + + $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password); + return $this->validate(); + + } + + /** + * Returns the username for the request + * + * @return string + */ + public function getUsername() { + + return $this->digestParts['username']; + + } + + /** + * Validates the digest challenge + * + * @return bool + */ + protected function validate() { + + $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri']; + + if ($this->digestParts['qop']=='auth-int') { + // Making sure we support this qop value + if (!($this->qop & self::QOP_AUTHINT)) return false; + // We need to add an md5 of the entire request body to the A2 part of the hash + $body = $this->httpRequest->getBody(true); + $this->httpRequest->setBody($body,true); + $A2 .= ':' . md5($body); + } else { + + // We need to make sure we support this qop value + if (!($this->qop & self::QOP_AUTH)) return false; + } + + $A2 = md5($A2); + + $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); + + return $this->digestParts['response']==$validResponse; + + + } + + /** + * Returns an HTTP 401 header, forcing login + * + * This should be called when username and password are incorrect, or not supplied at all + * + * @return void + */ + public function requireLogin() { + + $qop = ''; + switch($this->qop) { + case self::QOP_AUTH : $qop = 'auth'; break; + case self::QOP_AUTHINT : $qop = 'auth-int'; break; + case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break; + } + + $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"'); + $this->httpResponse->sendStatus(401); + + } + + + /** + * This method returns the full digest string. + * + * It should be compatibile with mod_php format and other webservers. + * + * If the header could not be found, null will be returned + * + * @return mixed + */ + public function getDigest() { + + // mod_php + $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST'); + if ($digest) return $digest; + + // most other servers + $digest = $this->httpRequest->getHeader('Authorization'); + + if ($digest && strpos(strtolower($digest),'digest')===0) { + return substr($digest,7); + } else { + return null; + } + + } + + + /** + * Parses the different pieces of the digest string into an array. + * + * This method returns false if an incomplete digest was supplied + * + * @param string $digest + * @return mixed + */ + protected function parseDigest($digest) { + + // protect against missing data + $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1); + $data = array(); + + preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER); + + foreach ($matches as $m) { + $data[$m[1]] = $m[2] ? $m[2] : $m[3]; + unset($needed_parts[$m[1]]); + } + + return $needed_parts ? false : $data; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php new file mode 100755 index 00000000..8a0059bc --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php @@ -0,0 +1,220 @@ +_SERVER = $serverData; + else $this->_SERVER =& $_SERVER; + + } + + /** + * Returns the value for a specific http header. + * + * This method returns null if the header did not exist. + * + * @param string $name + * @return string + */ + public function getHeader($name) { + + $serverName = 'HTTP_' . strtoupper(str_replace(array('-'),array('_'),$name)); + return isset($this->_SERVER[$serverName])?$this->_SERVER[$serverName]:null; + + } + + /** + * Returns all (known) HTTP headers. + * + * All headers are converted to lower-case, and additionally all underscores + * are automatically converted to dashes + * + * @return array + */ + public function getHeaders() { + + $hdrs = array(); + foreach($this->_SERVER as $key=>$value) { + + if (strpos($key,'HTTP_')===0) { + $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value; + } + + } + + return $hdrs; + + } + + /** + * Returns the HTTP request method + * + * This is for example POST or GET + * + * @return string + */ + public function getMethod() { + + return $this->_SERVER['REQUEST_METHOD']; + + } + + /** + * Returns the requested uri + * + * @return string + */ + public function getUri() { + + return $this->_SERVER['REQUEST_URI']; + + } + + /** + * Will return protocol + the hostname + the uri + * + * @return void + */ + public function getAbsoluteUri() { + + // Checking if the request was made through HTTPS. The last in line is for IIS + $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off'); + return ($protocol?'https':'http') . '://' . $this->getHeader('Host') . $this->getUri(); + + } + + /** + * Returns everything after the ? from the current url + * + * @return string + */ + public function getQueryString() { + + return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:''; + + } + + /** + * Returns the HTTP request body body + * + * This method returns a readable stream resource. + * If the asString parameter is set to true, a string is sent instead. + * + * @param bool asString + * @return resource + */ + public function getBody($asString = false) { + + if (is_null($this->body)) { + if (!is_null(self::$defaultInputStream)) { + $this->body = self::$defaultInputStream; + } else { + $this->body = fopen('php://input','r'); + self::$defaultInputStream = $this->body; + } + } + if ($asString) { + $body = stream_get_contents($this->body); + return $body; + } else { + return $this->body; + } + + } + + /** + * Sets the contents of the HTTP requet body + * + * This method can either accept a string, or a readable stream resource. + * + * If the setAsDefaultInputStream is set to true, it means for this run of the + * script the supplied body will be used instead of php://input. + * + * @param mixed $body + * @param bool $setAsDefaultInputStream + * @return void + */ + public function setBody($body,$setAsDefaultInputStream = false) { + + if(is_resource($body)) { + $this->body = $body; + } else { + + $stream = fopen('php://temp','r+'); + fputs($stream,$body); + rewind($stream); + // String is assumed + $this->body = $stream; + } + if ($setAsDefaultInputStream) { + self::$defaultInputStream = $this->body; + } + + } + + /** + * Returns a specific item from the _SERVER array. + * + * Do not rely on this feature, it is for internal use only. + * + * @param string $field + * @return string + */ + public function getRawServerValue($field) { + + return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null; + + } + +} + diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php new file mode 100755 index 00000000..15a26874 --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php @@ -0,0 +1,151 @@ + 'Continue', + 101 => 'Switching Protocols', + 102 => 'Processing', + 200 => 'Ok', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authorative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 207 => 'Multi-Status', // RFC 4918 + 208 => 'Already Reported', // RFC 5842 + 226 => 'IM Used', // RFC 3229 + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 306 => 'Reserved', + 307 => 'Temporary Redirect', + 400 => 'Bad request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed', + 418 => 'I\'m a teapot', // RFC 2324 + 422 => 'Unprocessable Entity', // RFC 4918 + 423 => 'Locked', // RFC 4918 + 424 => 'Failed Dependency', // RFC 4918 + 426 => 'Upgrade required', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version not supported', + 506 => 'Variant Also Negotiates', + 507 => 'Unsufficient Storage', // RFC 4918 + 508 => 'Loop Detected', // RFC 5842 + 510 => 'Not extended', + ); + + return 'HTTP/1.1 ' . $code . ' ' . $msg[$code]; + + } + + /** + * Sends an HTTP status header to the client + * + * @param int $code HTTP status code + * @return void + */ + public function sendStatus($code) { + + if (!headers_sent()) + return header($this->getStatusMessage($code)); + else return false; + + } + + /** + * Sets an HTTP header for the response + * + * @param string $name + * @param string $value + * @return void + */ + public function setHeader($name, $value, $replace = true) { + + $value = str_replace(array("\r","\n"),array('\r','\n'),$value); + if (!headers_sent()) + return header($name . ': ' . $value, $replace); + else return false; + + } + + /** + * Sets a bunch of HTTP Headers + * + * headersnames are specified as keys, value in the array value + * + * @param array $headers + * @return void + */ + public function setHeaders(array $headers) { + + foreach($headers as $key=>$value) + $this->setHeader($key, $value); + + } + + /** + * Sends the entire response body + * + * This method can accept either an open filestream, or a string. + * + * @param mixed $body + * @return void + */ + public function sendBody($body) { + + if (is_resource($body)) { + + fpassthru($body); + + } else { + + // We assume a string + echo $body; + + } + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php new file mode 100755 index 00000000..02ae1c0f --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php @@ -0,0 +1,65 @@ += 0) + return new DateTime('@' . $realDate, new DateTimeZone('UTC')); + + return false; + + } + +} diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php b/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php new file mode 100755 index 00000000..5372a49c --- /dev/null +++ b/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php @@ -0,0 +1,24 @@ + + +
                        +: +
                        + From 8dc2bf9a0aa15786a4ff58f52f1e9d85b6fe98db Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Wed, 15 Dec 2010 09:09:47 +0800 Subject: [PATCH 137/300] Reworked the entire WebDAV glue in order to get the move functionality to work properly. Cleanups. --- 3.0/modules/webdav/controllers/webdav.php | 209 ++++++++++++++++------ 1 file changed, 158 insertions(+), 51 deletions(-) diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php index 6d2c443a..b030c425 100644 --- a/3.0/modules/webdav/controllers/webdav.php +++ b/3.0/modules/webdav/controllers/webdav.php @@ -6,15 +6,19 @@ include 'Sabre/autoload.php'; class webdav_Controller extends Controller { public function gallery() { - $tree = new Gallery3Album(''); + $root = new Gallery3Album(''); + $tree = new Gallery3DAVTree($root); - $lock_backend = new Sabre_DAV_Locks_Backend_FS(TMPPATH . 'sabredav'); - $lock = new Sabre_DAV_Locks_Plugin($lock_backend); + // Skip the lock plugin for now, we don't want Finder to get write support for the time being. + //$lock_backend = new Sabre_DAV_Locks_Backend_FS(TMPPATH . 'sabredav'); + //$lock = new Sabre_DAV_Locks_Plugin($lock_backend); $filter = new Sabre_DAV_TemporaryFileFilterPlugin(TMPPATH . 'sabredav'); $server = new Sabre_DAV_Server($tree); + #$server = new Gallery3DAV($tree); + $server->setBaseUri(url::site('webdav/gallery')); - $server->addPlugin($lock); + //$server->addPlugin($lock); $server->addPlugin($filter); $this->doAuthenticate(); @@ -43,50 +47,161 @@ class webdav_Controller extends Controller { } } +class Gallery3DAVCache { + protected static $cache; + private static $instance; + + private function __construct() { + $this->cache = array(); + } + + private function encodePath($path) + { + $path = trim($path, '/'); + $encodedArray = array(); + foreach (split('/', $path) as $part) + { + $encodedArray[] = rawurlencode($part); + } + + $path = join('/', $encodedArray); + + return $path; + } + + public function getAlbumOf($path) { + $path = substr($path, 0, strrpos($path, '/')); + + return $this->getItemAt($path); + } + + public function getItemAt($path) + { + $path = trim($path, '/'); + $path = $this->encodePath($path); + + if (isset($this->cache[$path])) { + return $this->cache[$path]; + } + + $item = ORM::factory("item") + ->where("relative_path_cache", "=", $path) + ->find(); + + $this->cache[$path] = $item; + return $item; + } + + public static function singleton() { + if (!isset(self::$instance)) { + $c = __CLASS__; + self::$instance = new $c; + } + + return self::$instance; + } + + public function __clone() {} + +} + +class Gallery3DAVTree extends Sabre_DAV_Tree { + protected $rootNode; + + public function __construct(Sabre_DAV_ICollection $rootNode) { + $this->cache = Gallery3DAVCache::singleton(); + $this->rootNode = $rootNode; + } + + + public function move($source, $target) { + $sourceItem = $this->cache->getItemAt($source); + $targetItem = $this->cache->getAlbumOf($target); + + if (! access::can('view', $sourceItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('edit', $sourceItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('view', $targetItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('edit', $targetItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + + $sourceItem->parent_id = $targetItem->id; + $sourceItem->save(); + return true; + } + + public function getNodeForPath($path) { + + $path = trim($path,'/'); + + $currentNode = $this->rootNode; + $item = $this->cache->getItemAt($path); + + if (! $item->id) { + throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path); + } + + if ($item->type == 'album') { $currentNode = new Gallery3Album($path); } + else { $currentNode = new Gallery3File($path); } + + return $currentNode; + } +} class Gallery3Album extends Sabre_DAV_Directory { private $item; private $stat; + private $path; - function __construct($name) { - $this->item = ORM::factory("item") - ->where("relative_path_cache", "=", rawurlencode($name)) - ->find(); + function __construct($path) { + $this->cache = Gallery3DAVCache::singleton(); + $this->path = $path; + $this->item = $this->cache->getItemAt($path); } function getName() { return $this->item->name; } - function getChild($name) { - $rp = $this->item->relative_path() . '/' . rawurlencode($name); - if (substr($rp,0,1) == '/') { $rp = substr($rp, 1); } + function getChildren() { + $return = array(); + foreach ($this->item->children() as $child) { + $item = $this->getChild($child->name); + if ($item != false) { + $return[] = $item; + } + } + return $return; + } + + function getChild($name) { + $rp = $this->path . '/' . $name; - $child = ORM::factory("item") - ->where("relative_path_cache", "=", $rp) - ->find(); + $child = $this->cache->getItemAt($rp); + + if (! $child->id) { + throw new Sabre_DAV_Exception_FileNotFound('Access denied'); + } if (! access::can('view', $child)) { - return false; + return false; }; if ($child->type == 'album') { - return new Gallery3Album($this->item->relative_path() . $child->name); + return new Gallery3Album($rp); } else { return new Gallery3File($rp); } } - + public function createFile($name, $data = null) { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; - if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; if (substr($name, 0, 1) == '.') { return true; }; $tempfile = tempnam(TMPPATH, 'dav'); $target = fopen($tempfile, 'wb'); stream_copy_to_stream($data, $target); fclose($target); - + $parent_id = $this->item->__get('id'); $item = ORM::factory("item"); $item->name = $name; @@ -99,8 +214,8 @@ class Gallery3Album extends Sabre_DAV_Directory { } public function createDirectory($name) { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; - if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; + if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $parent_id = $this->item->__get('id'); $album = ORM::factory("item"); @@ -114,28 +229,21 @@ class Gallery3Album extends Sabre_DAV_Directory { $this->item = ORM::factory("item")->where('id', '=', $parent_id); } + function getLastModified() { + return $this->item->updated; + } + function setName($name) { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->name = $name; $this->item->save(); } public function delete() { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->delete(); } - - function getChildren() { - $return = array(); - foreach ($this->item->children() as $child) { - $item = $this->getChild($child->name); - if ($item != false) { - $return[] = $item; - } - } - return $return; - } } class Gallery3File extends Sabre_DAV_File { @@ -144,36 +252,35 @@ class Gallery3File extends Sabre_DAV_File { private $path; function __construct($path) { - $this->item = ORM::factory("item") - ->where("relative_path_cache", "=", $path) - ->find(); - - if (access::can('view_full', $this->item)) { - $this->stat = stat($this->item->file_path()); - $this->path = $this->item->file_path(); - } else { - $this->stat = stat($this->item->resize_path()); - $this->path = $this->item->resize_path(); - } + $this->cache = Gallery3DAVCache::singleton(); + $this->item = $this->cache->getItemAt($path); + + if (access::can('view_full', $this->item)) { + $this->stat = stat($this->item->file_path()); + $this->path = $this->item->file_path(); + } else { + $this->stat = stat($this->item->resize_path()); + $this->path = $this->item->resize_path(); + } } public function delete() { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->delete(); } function setName($name) { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; $this->item->name = $name; $this->item->save(); } public function getLastModified() { - return $this->stat[9]; + return $this->item->updated; } function get() { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; return fopen($this->path,'r'); } @@ -186,7 +293,7 @@ class Gallery3File extends Sabre_DAV_File { } function getETag() { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_FileNotFound('Access denied'); }; + if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; return '"' . md5($this->item->file_path()) . '"'; } } From c6e73030d9deacef373da6c60abed2cd9f7a2f3b Mon Sep 17 00:00:00 2001 From: Kriss Andsten Date: Fri, 17 Dec 2010 00:16:24 +0800 Subject: [PATCH 138/300] Optimizations. Still fairly ugly code, but it's quicker ugly code now. --- 3.0/modules/unrest/helpers/unrest_rest.php | 48 ++++++++++++---------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/3.0/modules/unrest/helpers/unrest_rest.php b/3.0/modules/unrest/helpers/unrest_rest.php index c16d4d4f..3e82a47c 100644 --- a/3.0/modules/unrest/helpers/unrest_rest.php +++ b/3.0/modules/unrest/helpers/unrest_rest.php @@ -1,14 +1,14 @@ 'name', @@ -22,7 +22,7 @@ class unrest_rest_Core { return $limit; } - private function getBasicLimiters($request, $limit = array()) + private static function getBasicLimiters($request, $limit = array()) { $directMapping = array( 'type' => 'type', @@ -37,7 +37,7 @@ class unrest_rest_Core { return $limit; } - private function albumsICanAccess() + private static function albumsICanAccess() { $db = db::build(); $gids = identity::group_ids_for_active_user(); @@ -136,7 +136,7 @@ class unrest_rest_Core { } } - static function addChildren($request, $db, $filler, $permitted, $display, &$return) + static function addChildren($request, $db, $filler, $permitted, $display, &$return, $rest_base) { $children = $db->select('parent_id', 'id')->from('items')->where('parent_id', 'IN', $filler['children_of']); if (isset($request->params->childtypes)) @@ -165,7 +165,7 @@ class unrest_rest_Core { else { $members = array(); foreach ($childBlock[ $data['entity']['id'] ] as $child) { - $members[] = unrest_rest::makeRestURL('item', $child); + $members[] = unrest_rest::makeRestURL('item', $child, $rest_base); } $data['members'] = $members; } @@ -177,13 +177,13 @@ class unrest_rest_Core { } } - private function makeRestURL($resource, $identifier) + private static function makeRestURL($resource, $identifier, $base) { - return url::abs_site("rest") . '/' . $resource . '/' . $identifier; + return $base . '/' . $resource . '/' . $identifier; } - public function size_url($size, $relative_path_cache, $type) { - $base = url::abs_file('var/' . $size . '/' ) . $relative_path_cache; + public static function size_url($size, $relative_path_cache, $type, $file_base) { + $base = $file_base . 'var/' . $size . '/' . $relative_path_cache; if ($type == 'photo') { return $base; } else if ($type == 'album') { @@ -193,17 +193,18 @@ class unrest_rest_Core { return preg_replace("/...$/", "jpg", $base); } } - - + + static function get($request) { $db = db::build(); + $start = microtime(true); + $rest_base = url::abs_site("rest"); + $file_base = url::abs_file(''); #'var/' . $size . '/' /* Build basic limiters */ $limit = unrest_rest::getBasicLimiters($request); $limit = unrest_rest::getFreetextLimiters($request,$limit); - error_log(print_r($limit,1)); - /* Build numeric limiters */ /* ...at some point. */ @@ -257,21 +258,21 @@ class unrest_rest_Core { 'thumb_width' => $item->resize_width ); - $ui['thumb_url_public'] = unrest_rest::size_url('thumbs', $item->relative_path_cache, $item->type); + $ui['thumb_url_public'] = unrest_rest::size_url('thumbs', $item->relative_path_cache, $item->type, $file_base); $public = $item->view_1?true:false; $fullPublic = $item->view_full_1?true:false; if ($item->type != 'album') { - $ui['file_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=full'); - $ui['thumb_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=thumb'); - $ui['resize_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=resize'); + $ui['file_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=full', $rest_base); + $ui['thumb_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=thumb', $rest_base); + $ui['resize_url'] = unrest_rest::makeRestURL('data', $item->id . '?size=resize', $rest_base); if ($public) { - $ui['resize_url_public'] = unrest_rest::size_url('resizes', $item->relative_path_cache, $item->type); + $ui['resize_url_public'] = unrest_rest::size_url('resizes', $item->relative_path_cache, $item->type, $file_base); if ($fullPublic) { - $ui['file_url_public'] = unrest_rest::size_url('albums', $item->relative_path_cache, $item->type); + $ui['file_url_public'] = unrest_rest::size_url('albums', $item->relative_path_cache, $item->type, $file_base); } } } @@ -284,7 +285,7 @@ class unrest_rest_Core { } $return[] = array( - 'url' => unrest_rest::makeRestURL('item', $item->id ), + 'url' => unrest_rest::makeRestURL('item', $item->id, $rest_base ), 'entity' => $data ); } @@ -293,9 +294,12 @@ class unrest_rest_Core { /* Do we need to fetch children? */ if (array_key_exists('children_of', $filler)) { - unrest_rest::addChildren($request, $db, $filler, $permitted, $display, &$return); + unrest_rest::addChildren($request, $db, $filler, $permitted, $display, &$return, $rest_base); } + $end = microtime(true); + error_log("Inner " . ($end - $start) . " seconds taken"); + return $return; } } From bfda243ce08701448479a8bd3a35f9ed164e83a8 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 16 Dec 2010 15:03:54 -0800 Subject: [PATCH 139/300] Restore original whitespace --- 3.0/modules/aws_s3/helpers/aws_s3.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/3.0/modules/aws_s3/helpers/aws_s3.php b/3.0/modules/aws_s3/helpers/aws_s3.php index 54c1ebc1..891ba0d3 100644 --- a/3.0/modules/aws_s3/helpers/aws_s3.php +++ b/3.0/modules/aws_s3/helpers/aws_s3.php @@ -38,12 +38,12 @@ class aws_s3_Core { pack('H*', sha1((str_pad(module::get_var("aws_s3", "secret_key"), 64, chr(0x00)) ^ (str_repeat(chr(0x36), 64))) . $string))))); } - + static function generate_url($resource, $authenticated = false, $updated = null) { $find = array("{guid}", "{bucket}", "{resource}"); $replace = array(module::get_var("aws_s3", "g3id"), module::get_var("aws_s3", "bucket_name"), $resource); $url = str_replace($find, $replace, module::get_var("aws_s3", "url_str")); - + if ($authenticated) { preg_match("%https?://([a-zA-Z0-9\.-]*)/(.*)$%", $url, $matches); $host = module::get_var("aws_s3" , "bucket_name"); @@ -51,7 +51,7 @@ class aws_s3_Core { $url .= "?AWSAccessKeyId=" . module::get_var("aws_s3", "access_key") . "&Expires=" . (time() + module::get_var("aws_s3", "sig_exp")) . "&Signature=" . urlencode(self::getHash("GET\n\n\n" . (time() + module::get_var("aws_s3", "sig_exp")) . "\n/" . $host . "/" . $resource)); - + self::get_s3(); S3::getAuthenticatedURL(module::get_var("aws_s3", "bucket_name"), $resource, module::get_var("aws_s3", "sig_exp")); } From 0f6fbf7bb234440ba6a2ac933aa32e057322a625 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 16 Dec 2010 15:25:11 -0800 Subject: [PATCH 140/300] Copy this change to 3.0 > commit 30700cb8de1293b442b73dae462e3fd2e6b4bf31 > Author: Bharat Mediratta > Date: Sat Nov 27 18:35:11 2010 -0800 > > Oops, add in security. Only show viewable children. --- 3.0/modules/albumtree/views/albumtree_block.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/modules/albumtree/views/albumtree_block.html.php b/3.0/modules/albumtree/views/albumtree_block.html.php index 7cf66799..4a73c333 100644 --- a/3.0/modules/albumtree/views/albumtree_block.html.php +++ b/3.0/modules/albumtree/views/albumtree_block.html.php @@ -10,7 +10,7 @@ -children(null, null, array(array("type", "=", "album"))) as $child): ?> +viewable()->children(null, null, array(array("type", "=", "album"))) as $child): ?> From 2609cae1902ad32a2975baa44642997a23923366 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 17 Dec 2010 13:25:40 -0800 Subject: [PATCH 141/300] Clean up style and whitespace to conform to Gallery 3 conventions. No major structural changes. Added some missing security. --- 3.0/modules/webdav/controllers/webdav.php | 573 +++++++++++----------- 1 file changed, 297 insertions(+), 276 deletions(-) diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php index b030c425..56bc388d 100644 --- a/3.0/modules/webdav/controllers/webdav.php +++ b/3.0/modules/webdav/controllers/webdav.php @@ -1,301 +1,322 @@ setBaseUri(url::site('webdav/gallery')); - //$server->addPlugin($lock); - $server->addPlugin($filter); - - $this->doAuthenticate(); - $server->exec(); + $server->setBaseUri(url::site("/")); + // $server->addPlugin($lock); + $server->addPlugin($filter); + + if ($this->_authenticate()) { + $server->exec(); + } } - - private function doAuthenticate() { - $auth = new Sabre_HTTP_BasicAuth(); - $auth->setRealm('Gallery3'); - $authResult = $auth->getUserPass(); - list($username, $password) = $authResult; - - if ($username == '' || $password == '') { - $auth->requireLogin(); - die; - } - - $user = identity::lookup_user_by_name($username); - if (empty($user) || !identity::is_correct_password($user, $password)) { - $auth->requireLogin(); - die; - } - - identity::set_active_user($user); - return $user; + + private function _authenticate() { + $auth = new Sabre_HTTP_BasicAuth(); + $auth->setRealm(item::root()->title); + $authResult = $auth->getUserPass(); + list($username, $password) = $authResult; + + if (!$username || !$password) { + $auth->requireLogin(); + return false; + } + + $user = identity::lookup_user_by_name($username); + if (empty($user) || !identity::is_correct_password($user, $password)) { + $auth->requireLogin(); + return false; + } + + identity::set_active_user($user); + return true; } } -class Gallery3DAVCache { - protected static $cache; - private static $instance; - - private function __construct() { - $this->cache = array(); - } +class Gallery3_DAV_Cache { + private static $cache; + private static $instance; - private function encodePath($path) - { - $path = trim($path, '/'); - $encodedArray = array(); - foreach (split('/', $path) as $part) - { - $encodedArray[] = rawurlencode($part); - } - - $path = join('/', $encodedArray); - - return $path; - } + private function __construct() { + self::$cache = array(); + } - public function getAlbumOf($path) { - $path = substr($path, 0, strrpos($path, '/')); - - return $this->getItemAt($path); + public static function instance() { + if (!isset(self::$instance)) { + self::$instance = new Gallery3_DAV_Cache(); } - - public function getItemAt($path) - { - $path = trim($path, '/'); - $path = $this->encodePath($path); - - if (isset($this->cache[$path])) { - return $this->cache[$path]; - } - - $item = ORM::factory("item") + return self::$instance; + } + + private function encode_path($path) { + $path = trim($path, "/"); + $encoded_array = array(); + foreach (explode("/", $path) as $part) { + $encoded_array[] = rawurlencode($part); + } + + return join("/", $encoded_array); + } + + public function to_album($path) { + $path = substr($path, 0, strrpos($path, "/")); + return $this->to_item($path); + } + + public function to_item($path) { + $path = trim($path, "/"); + $path = $this->encode_path($path); + + if (!isset(self::$cache[$path])) { + self::$cache[$path] = ORM::factory("item") + ->viewable() ->where("relative_path_cache", "=", $path) ->find(); - - $this->cache[$path] = $item; - return $item; } - - public static function singleton() { - if (!isset(self::$instance)) { - $c = __CLASS__; - self::$instance = new $c; - } - - return self::$instance; - } - - public function __clone() {} - + + return self::$cache[$path]; + } + + public function __clone() { + } } -class Gallery3DAVTree extends Sabre_DAV_Tree { - protected $rootNode; - - public function __construct(Sabre_DAV_ICollection $rootNode) { - $this->cache = Gallery3DAVCache::singleton(); - $this->rootNode = $rootNode; +class Gallery3_DAV_Tree extends Sabre_DAV_Tree { + protected $root_node; + + public function __construct(Sabre_DAV_ICollection $root_node) { + $this->cache = Gallery3_DAV_Cache::instance(); + $this->root_node = $root_node; + } + + public function move($source, $target) { + $source_item = $this->cache->to_item($source); + $target_item = $this->cache->to_album($target); + + try { + access::required("view", $sourceItem); + access::required("edit", $sourceItem); + access::required("view", $targetItem); + access::required("edit", $targetItem); + } catch (Kohana_404_Exception $e) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); } - - - public function move($source, $target) { - $sourceItem = $this->cache->getItemAt($source); - $targetItem = $this->cache->getAlbumOf($target); - - if (! access::can('view', $sourceItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - if (! access::can('edit', $sourceItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - if (! access::can('view', $targetItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - if (! access::can('edit', $targetItem)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - - $sourceItem->parent_id = $targetItem->id; - $sourceItem->save(); + + $source_item->parent_id = $targetItem->id; + $source_item->save(); + return true; + } + + public function getNodeForPath($path) { + $path = trim($path,"/"); + $item = $this->cache->to_item($path); + + if (!$item->loaded()) { + throw new Sabre_DAV_Exception_FileNotFound("Could not find node at path: $path"); + } + + if ($item->is_album()) { + return new Gallery3_Album($path); + } else { + return new Gallery3_File($path); + } + } +} + +class Gallery3_Album extends Sabre_DAV_Directory { + private $item; + private $stat; + private $path; + + function __construct($path) { + $this->cache = Gallery3_DAV_Cache::instance(); + $this->path = $path; + $this->item = $this->cache->to_item($path); + } + + function getName() { + return $this->item->name; + } + + function getChildren() { + $return = array(); + foreach ($this->item->viewable()->children() as $child) { + $return[] = $this->getChild($child->name); + } + return $return; + } + + function getChild($name) { + $rp = "{$this->path}/$name"; + $child = $this->cache->to_item($rp); + + if (!access::can("view", $child)) { + throw new Sabre_DAV_Exception_FileNotFound("Access denied"); + } + + if ($child->is_album()) { + return new Gallery3_Album($rp); + } else { + return new Gallery3_File($rp); + } + } + + public function createFile($name, $data=null) { + try { + access::required("view", $this->item); + access::required("add", $this->item); + } catch (Kohana_404_Exception $e) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + } + if (substr($name, 0, 1) == ".") { return true; + }; + + try { + $tempfile = tempnam(TMPPATH, "dav"); + $target = fopen($tempfile, "wb"); + stream_copy_to_stream($data, $target); + fclose($target); + + $item = ORM::factory("item"); + $item->name = $name; + $item->title = item::convert_filename_to_title($item->name); + $item->description = ""; + $item->parent_id = $this->item->id; + $item->set_data_file($tempfile); + $item->type = "photo"; + $item->save(); + } catch (Exception $e) { + unlink($tempfile); + throw $e; } - - public function getNodeForPath($path) { - - $path = trim($path,'/'); - - $currentNode = $this->rootNode; - $item = $this->cache->getItemAt($path); - - if (! $item->id) { - throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path); - } - - if ($item->type == 'album') { $currentNode = new Gallery3Album($path); } - else { $currentNode = new Gallery3File($path); } - - return $currentNode; + } + + public function createDirectory($name) { + try { + access::required("view", $this->item); + access::required("add", $this->item); + } catch (Kohana_404_Exception $e) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); } + + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $this->item->id; + $album->name = $name; + $album->title = $name; + $album->description = ""; + $album->save(); + + // Refresh MPTT pointers + $this->item->reload(); + } + + function getLastModified() { + return $this->item->updated; + } + + function setName($name) { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + + $this->item->name = $name; + $this->item->save(); + } + + public function delete() { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + $this->item->delete(); + } } -class Gallery3Album extends Sabre_DAV_Directory { - private $item; - private $stat; - private $path; - - function __construct($path) { - $this->cache = Gallery3DAVCache::singleton(); - $this->path = $path; - $this->item = $this->cache->getItemAt($path); - } - - function getName() { - return $this->item->name; - } - - function getChildren() { - $return = array(); - foreach ($this->item->children() as $child) { - $item = $this->getChild($child->name); - if ($item != false) { - $return[] = $item; - } - } - return $return; - } - - function getChild($name) { - $rp = $this->path . '/' . $name; - - $child = $this->cache->getItemAt($rp); - - if (! $child->id) { - throw new Sabre_DAV_Exception_FileNotFound('Access denied'); - } - - if (! access::can('view', $child)) { - return false; - }; - - if ($child->type == 'album') { - return new Gallery3Album($rp); - } else { - return new Gallery3File($rp); - } - } - - public function createFile($name, $data = null) { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - if (substr($name, 0, 1) == '.') { return true; }; - - $tempfile = tempnam(TMPPATH, 'dav'); - $target = fopen($tempfile, 'wb'); - stream_copy_to_stream($data, $target); - fclose($target); - - $parent_id = $this->item->__get('id'); - $item = ORM::factory("item"); - $item->name = $name; - $item->title = item::convert_filename_to_title($item->name); - $item->description = ''; - $item->parent_id = $parent_id; - $item->set_data_file($tempfile); - $item->type = "photo"; - $item->save(); - } - - public function createDirectory($name) { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - if (! access::can('add', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - - $parent_id = $this->item->__get('id'); - $album = ORM::factory("item"); - $album->type = "album"; - $album->parent_id = $parent_id; - $album->name = $name; - $album->title = $name; - $album->description = ''; - $album->save(); - - $this->item = ORM::factory("item")->where('id', '=', $parent_id); - } - - function getLastModified() { - return $this->item->updated; - } - - function setName($name) { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - - $this->item->name = $name; - $this->item->save(); - } - - public function delete() { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - $this->item->delete(); - } -} +class Gallery3_File extends Sabre_DAV_File { + private $item; + private $stat; + private $path; -class Gallery3File extends Sabre_DAV_File { - private $item; - private $stat; - private $path; - - function __construct($path) { - $this->cache = Gallery3DAVCache::singleton(); - $this->item = $this->cache->getItemAt($path); - - if (access::can('view_full', $this->item)) { - $this->stat = stat($this->item->file_path()); - $this->path = $this->item->file_path(); - } else { - $this->stat = stat($this->item->resize_path()); - $this->path = $this->item->resize_path(); - } - } - - public function delete() { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - $this->item->delete(); - } - - function setName($name) { - if (! access::can('edit', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - $this->item->name = $name; - $this->item->save(); - } - - public function getLastModified() { - return $this->item->updated; - } - - function get() { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - return fopen($this->path,'r'); - } - - function getSize() { - return $this->stat[7]; - } - - function getName() { - return $this->item->name; - } - - function getETag() { - if (! access::can('view', $this->item)) { throw new Sabre_DAV_Exception_Forbidden('Access denied'); }; - return '"' . md5($this->item->file_path()) . '"'; - } -} + function __construct($path) { + $this->cache = Gallery3_DAV_Cache::instance(); + $this->item = $this->cache->to_item($path); -?> \ No newline at end of file + if (access::can("view_full", $this->item)) { + $this->stat = stat($this->item->file_path()); + $this->path = $this->item->file_path(); + } else { + $this->stat = stat($this->item->resize_path()); + $this->path = $this->item->resize_path(); + } + } + + public function delete() { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + $this->item->delete(); + } + + function setName($name) { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + $this->item->name = $name; + $this->item->save(); + } + + public function getLastModified() { + return $this->item->updated; + } + + function get() { + if (!access::can("view", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + return fopen($this->path, "r"); + } + + function getSize() { + return $this->stat[7]; + } + + function getName() { + return $this->item->name; + } + + function getETag() { + if (!access::can("view", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + return "'" . md5($this->item->file_path()) . "'"; + } +} From 7d42a4239a2726fade3d6e2d044ac0bcac37838c Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 17 Dec 2010 14:42:57 -0800 Subject: [PATCH 142/300] Move vendor code into vendor directory; libraries is for Gallery3 code. --- 3.0/modules/webdav/{libraries => vendor}/Sabre.autoload.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre.includes.php | 0 .../{libraries => vendor}/Sabre/CalDAV/Backend/Abstract.php | 0 .../webdav/{libraries => vendor}/Sabre/CalDAV/Backend/PDO.php | 0 .../webdav/{libraries => vendor}/Sabre/CalDAV/Calendar.php | 0 .../webdav/{libraries => vendor}/Sabre/CalDAV/CalendarObject.php | 0 .../{libraries => vendor}/Sabre/CalDAV/CalendarRootNode.php | 0 .../Sabre/CalDAV/Exception/InvalidICalendarObject.php | 0 .../webdav/{libraries => vendor}/Sabre/CalDAV/ICalendarUtil.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Plugin.php | 0 .../Sabre/CalDAV/Property/SupportedCalendarComponentSet.php | 0 .../Sabre/CalDAV/Property/SupportedCalendarData.php | 0 .../Sabre/CalDAV/Property/SupportedCollationSet.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Server.php | 0 .../webdav/{libraries => vendor}/Sabre/CalDAV/UserCalendars.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Version.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/XMLUtil.php | 0 .../{libraries => vendor}/Sabre/DAV/Auth/Backend/Abstract.php | 0 .../Sabre/DAV/Auth/Backend/AbstractBasic.php | 0 .../Sabre/DAV/Auth/Backend/AbstractDigest.php | 0 .../{libraries => vendor}/Sabre/DAV/Auth/Backend/Apache.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/File.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/PDO.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Auth/Plugin.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Auth/Principal.php | 0 .../{libraries => vendor}/Sabre/DAV/Auth/PrincipalCollection.php | 0 .../{libraries => vendor}/Sabre/DAV/Browser/GuessContentType.php | 0 .../{libraries => vendor}/Sabre/DAV/Browser/MapGetToPropFind.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Browser/Plugin.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Directory.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception.php | 0 .../{libraries => vendor}/Sabre/DAV/Exception/BadRequest.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Exception/Conflict.php | 0 .../{libraries => vendor}/Sabre/DAV/Exception/ConflictingLock.php | 0 .../{libraries => vendor}/Sabre/DAV/Exception/FileNotFound.php | 0 .../{libraries => vendor}/Sabre/DAV/Exception/Forbidden.php | 0 .../Sabre/DAV/Exception/InsufficientStorage.php | 0 .../Sabre/DAV/Exception/InvalidResourceType.php | 0 .../Sabre/DAV/Exception/LockTokenMatchesRequestUri.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Exception/Locked.php | 0 .../Sabre/DAV/Exception/MethodNotAllowed.php | 0 .../Sabre/DAV/Exception/NotAuthenticated.php | 0 .../{libraries => vendor}/Sabre/DAV/Exception/NotImplemented.php | 0 .../Sabre/DAV/Exception/PreconditionFailed.php | 0 .../Sabre/DAV/Exception/ReportNotImplemented.php | 0 .../Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php | 0 .../Sabre/DAV/Exception/UnsupportedMediaType.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/FS/Directory.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FS/File.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FS/Node.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/FSExt/Directory.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FSExt/File.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FSExt/Node.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/File.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/ICollection.php | 0 .../{libraries => vendor}/Sabre/DAV/IExtendedCollection.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/IFile.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/ILockable.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/INode.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/IProperties.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/IQuota.php | 0 .../{libraries => vendor}/Sabre/DAV/Locks/Backend/Abstract.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Locks/Backend/FS.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Locks/Backend/PDO.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Locks/LockInfo.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Locks/Plugin.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Mount/Plugin.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Node.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/ObjectTree.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property.php | 0 .../{libraries => vendor}/Sabre/DAV/Property/GetLastModified.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Property/Href.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Property/IHref.php | 0 .../{libraries => vendor}/Sabre/DAV/Property/LockDiscovery.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Property/Principal.php | 0 .../{libraries => vendor}/Sabre/DAV/Property/ResourceType.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Property/Response.php | 0 .../{libraries => vendor}/Sabre/DAV/Property/SupportedLock.php | 0 .../Sabre/DAV/Property/SupportedReportSet.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Server.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/ServerPlugin.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/SimpleDirectory.php | 0 .../{libraries => vendor}/Sabre/DAV/TemporaryFileFilterPlugin.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Tree.php | 0 .../webdav/{libraries => vendor}/Sabre/DAV/Tree/Filesystem.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/URLUtil.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Version.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/XMLUtil.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/AWSAuth.php | 0 .../webdav/{libraries => vendor}/Sabre/HTTP/AbstractAuth.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/BasicAuth.php | 0 .../webdav/{libraries => vendor}/Sabre/HTTP/DigestAuth.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Request.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Response.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Util.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Version.php | 0 3.0/modules/webdav/{libraries => vendor}/Sabre/autoload.php | 0 97 files changed, 0 insertions(+), 0 deletions(-) rename 3.0/modules/webdav/{libraries => vendor}/Sabre.autoload.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre.includes.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Backend/Abstract.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Backend/PDO.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Calendar.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/CalendarObject.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/CalendarRootNode.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Exception/InvalidICalendarObject.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/ICalendarUtil.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Plugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Property/SupportedCalendarData.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Property/SupportedCollationSet.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Server.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/UserCalendars.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/Version.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/CalDAV/XMLUtil.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/Abstract.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/AbstractBasic.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/AbstractDigest.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/Apache.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/File.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Backend/PDO.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Plugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/Principal.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Auth/PrincipalCollection.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Browser/GuessContentType.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Browser/MapGetToPropFind.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Browser/Plugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Directory.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/BadRequest.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/Conflict.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/ConflictingLock.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/FileNotFound.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/Forbidden.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/InsufficientStorage.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/InvalidResourceType.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/Locked.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/MethodNotAllowed.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/NotAuthenticated.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/NotImplemented.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/PreconditionFailed.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/ReportNotImplemented.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Exception/UnsupportedMediaType.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FS/Directory.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FS/File.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FS/Node.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FSExt/Directory.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FSExt/File.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/FSExt/Node.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/File.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/ICollection.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/IExtendedCollection.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/IFile.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/ILockable.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/INode.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/IProperties.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/IQuota.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Locks/Backend/Abstract.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Locks/Backend/FS.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Locks/Backend/PDO.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Locks/LockInfo.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Locks/Plugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Mount/Plugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Node.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/ObjectTree.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/GetLastModified.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/Href.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/IHref.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/LockDiscovery.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/Principal.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/ResourceType.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/Response.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/SupportedLock.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Property/SupportedReportSet.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Server.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/ServerPlugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/SimpleDirectory.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/TemporaryFileFilterPlugin.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Tree.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Tree/Filesystem.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/URLUtil.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/Version.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/DAV/XMLUtil.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/AWSAuth.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/AbstractAuth.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/BasicAuth.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/DigestAuth.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Request.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Response.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Util.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/HTTP/Version.php (100%) rename 3.0/modules/webdav/{libraries => vendor}/Sabre/autoload.php (100%) diff --git a/3.0/modules/webdav/libraries/Sabre.autoload.php b/3.0/modules/webdav/vendor/Sabre.autoload.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre.autoload.php rename to 3.0/modules/webdav/vendor/Sabre.autoload.php diff --git a/3.0/modules/webdav/libraries/Sabre.includes.php b/3.0/modules/webdav/vendor/Sabre.includes.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre.includes.php rename to 3.0/modules/webdav/vendor/Sabre.includes.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/Abstract.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Backend/Abstract.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/Abstract.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Backend/Abstract.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Backend/PDO.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Backend/PDO.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Backend/PDO.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Calendar.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Calendar.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Calendar.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/CalendarObject.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarObject.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/CalendarObject.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/CalendarRootNode.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/CalendarRootNode.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/CalendarRootNode.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Exception/InvalidICalendarObject.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Exception/InvalidICalendarObject.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Exception/InvalidICalendarObject.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/ICalendarUtil.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/ICalendarUtil.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/ICalendarUtil.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/ICalendarUtil.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Plugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Plugin.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Plugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Property/SupportedCalendarData.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCalendarData.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Property/SupportedCalendarData.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Property/SupportedCollationSet.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Property/SupportedCollationSet.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Property/SupportedCollationSet.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Server.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Server.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Server.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/UserCalendars.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/UserCalendars.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/UserCalendars.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/Version.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/Version.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/Version.php diff --git a/3.0/modules/webdav/libraries/Sabre/CalDAV/XMLUtil.php b/3.0/modules/webdav/vendor/Sabre/CalDAV/XMLUtil.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/CalDAV/XMLUtil.php rename to 3.0/modules/webdav/vendor/Sabre/CalDAV/XMLUtil.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/Abstract.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Abstract.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/Abstract.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/AbstractBasic.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractBasic.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/AbstractBasic.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/AbstractDigest.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/AbstractDigest.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/AbstractDigest.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/Apache.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/Apache.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/Apache.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/File.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/File.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/File.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/PDO.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Backend/PDO.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Backend/PDO.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Plugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Plugin.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Plugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/Principal.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/Principal.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/Principal.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php b/3.0/modules/webdav/vendor/Sabre/DAV/Auth/PrincipalCollection.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Auth/PrincipalCollection.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Auth/PrincipalCollection.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php b/3.0/modules/webdav/vendor/Sabre/DAV/Browser/GuessContentType.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Browser/GuessContentType.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Browser/GuessContentType.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php b/3.0/modules/webdav/vendor/Sabre/DAV/Browser/MapGetToPropFind.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Browser/MapGetToPropFind.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Browser/MapGetToPropFind.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php b/3.0/modules/webdav/vendor/Sabre/DAV/Browser/Plugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Browser/Plugin.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Browser/Plugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Directory.php b/3.0/modules/webdav/vendor/Sabre/DAV/Directory.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Directory.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Directory.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/BadRequest.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/BadRequest.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/BadRequest.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/BadRequest.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Conflict.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/Conflict.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Conflict.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/Conflict.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ConflictingLock.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/ConflictingLock.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/ConflictingLock.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/ConflictingLock.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/FileNotFound.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/FileNotFound.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/FileNotFound.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Forbidden.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/Forbidden.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Forbidden.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/Forbidden.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/InsufficientStorage.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/InsufficientStorage.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/InsufficientStorage.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/InsufficientStorage.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/InvalidResourceType.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/InvalidResourceType.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/InvalidResourceType.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/InvalidResourceType.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/Locked.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/Locked.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/Locked.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/MethodNotAllowed.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/MethodNotAllowed.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/MethodNotAllowed.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/NotAuthenticated.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotAuthenticated.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/NotAuthenticated.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotImplemented.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/NotImplemented.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/NotImplemented.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/NotImplemented.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/PreconditionFailed.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/PreconditionFailed.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/PreconditionFailed.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/PreconditionFailed.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/ReportNotImplemented.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/ReportNotImplemented.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/ReportNotImplemented.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php b/3.0/modules/webdav/vendor/Sabre/DAV/Exception/UnsupportedMediaType.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Exception/UnsupportedMediaType.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Exception/UnsupportedMediaType.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/Directory.php b/3.0/modules/webdav/vendor/Sabre/DAV/FS/Directory.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/FS/Directory.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/FS/Directory.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php b/3.0/modules/webdav/vendor/Sabre/DAV/FS/File.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/FS/File.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/FS/File.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php b/3.0/modules/webdav/vendor/Sabre/DAV/FS/Node.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/FS/Node.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/FS/Node.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php b/3.0/modules/webdav/vendor/Sabre/DAV/FSExt/Directory.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Directory.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/FSExt/Directory.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php b/3.0/modules/webdav/vendor/Sabre/DAV/FSExt/File.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/File.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/FSExt/File.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php b/3.0/modules/webdav/vendor/Sabre/DAV/FSExt/Node.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/FSExt/Node.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/FSExt/Node.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/File.php b/3.0/modules/webdav/vendor/Sabre/DAV/File.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/File.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/File.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/ICollection.php b/3.0/modules/webdav/vendor/Sabre/DAV/ICollection.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/ICollection.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/ICollection.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/IExtendedCollection.php b/3.0/modules/webdav/vendor/Sabre/DAV/IExtendedCollection.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/IExtendedCollection.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/IExtendedCollection.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/IFile.php b/3.0/modules/webdav/vendor/Sabre/DAV/IFile.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/IFile.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/IFile.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/ILockable.php b/3.0/modules/webdav/vendor/Sabre/DAV/ILockable.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/ILockable.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/ILockable.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/INode.php b/3.0/modules/webdav/vendor/Sabre/DAV/INode.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/INode.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/INode.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/IProperties.php b/3.0/modules/webdav/vendor/Sabre/DAV/IProperties.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/IProperties.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/IProperties.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php b/3.0/modules/webdav/vendor/Sabre/DAV/IQuota.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/IQuota.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/IQuota.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/Abstract.php b/3.0/modules/webdav/vendor/Sabre/DAV/Locks/Backend/Abstract.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/Abstract.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Locks/Backend/Abstract.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/FS.php b/3.0/modules/webdav/vendor/Sabre/DAV/Locks/Backend/FS.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/FS.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Locks/Backend/FS.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php b/3.0/modules/webdav/vendor/Sabre/DAV/Locks/Backend/PDO.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Backend/PDO.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Locks/Backend/PDO.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php b/3.0/modules/webdav/vendor/Sabre/DAV/Locks/LockInfo.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Locks/LockInfo.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Locks/LockInfo.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Locks/Plugin.php b/3.0/modules/webdav/vendor/Sabre/DAV/Locks/Plugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Locks/Plugin.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Locks/Plugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php b/3.0/modules/webdav/vendor/Sabre/DAV/Mount/Plugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Mount/Plugin.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Mount/Plugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Node.php b/3.0/modules/webdav/vendor/Sabre/DAV/Node.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Node.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Node.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/ObjectTree.php b/3.0/modules/webdav/vendor/Sabre/DAV/ObjectTree.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/ObjectTree.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/ObjectTree.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/GetLastModified.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/GetLastModified.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/GetLastModified.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/GetLastModified.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/Href.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/Href.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/Href.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/IHref.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/IHref.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/IHref.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/LockDiscovery.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/LockDiscovery.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/LockDiscovery.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/LockDiscovery.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/Principal.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/Principal.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/Principal.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/ResourceType.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/ResourceType.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/ResourceType.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/Response.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/Response.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/Response.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/SupportedLock.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedLock.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/SupportedLock.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php b/3.0/modules/webdav/vendor/Sabre/DAV/Property/SupportedReportSet.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Property/SupportedReportSet.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Property/SupportedReportSet.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Server.php b/3.0/modules/webdav/vendor/Sabre/DAV/Server.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Server.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Server.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php b/3.0/modules/webdav/vendor/Sabre/DAV/ServerPlugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/ServerPlugin.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/ServerPlugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/SimpleDirectory.php b/3.0/modules/webdav/vendor/Sabre/DAV/SimpleDirectory.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/SimpleDirectory.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/SimpleDirectory.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php b/3.0/modules/webdav/vendor/Sabre/DAV/TemporaryFileFilterPlugin.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/TemporaryFileFilterPlugin.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/TemporaryFileFilterPlugin.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Tree.php b/3.0/modules/webdav/vendor/Sabre/DAV/Tree.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Tree.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Tree.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php b/3.0/modules/webdav/vendor/Sabre/DAV/Tree/Filesystem.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Tree/Filesystem.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Tree/Filesystem.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php b/3.0/modules/webdav/vendor/Sabre/DAV/URLUtil.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/URLUtil.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/URLUtil.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/Version.php b/3.0/modules/webdav/vendor/Sabre/DAV/Version.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/Version.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/Version.php diff --git a/3.0/modules/webdav/libraries/Sabre/DAV/XMLUtil.php b/3.0/modules/webdav/vendor/Sabre/DAV/XMLUtil.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/DAV/XMLUtil.php rename to 3.0/modules/webdav/vendor/Sabre/DAV/XMLUtil.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php b/3.0/modules/webdav/vendor/Sabre/HTTP/AWSAuth.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/AWSAuth.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/AWSAuth.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php b/3.0/modules/webdav/vendor/Sabre/HTTP/AbstractAuth.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/AbstractAuth.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/AbstractAuth.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php b/3.0/modules/webdav/vendor/Sabre/HTTP/BasicAuth.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/BasicAuth.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/BasicAuth.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php b/3.0/modules/webdav/vendor/Sabre/HTTP/DigestAuth.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/DigestAuth.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/DigestAuth.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Request.php b/3.0/modules/webdav/vendor/Sabre/HTTP/Request.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/Request.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/Request.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Response.php b/3.0/modules/webdav/vendor/Sabre/HTTP/Response.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/Response.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/Response.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Util.php b/3.0/modules/webdav/vendor/Sabre/HTTP/Util.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/Util.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/Util.php diff --git a/3.0/modules/webdav/libraries/Sabre/HTTP/Version.php b/3.0/modules/webdav/vendor/Sabre/HTTP/Version.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/HTTP/Version.php rename to 3.0/modules/webdav/vendor/Sabre/HTTP/Version.php diff --git a/3.0/modules/webdav/libraries/Sabre/autoload.php b/3.0/modules/webdav/vendor/Sabre/autoload.php similarity index 100% rename from 3.0/modules/webdav/libraries/Sabre/autoload.php rename to 3.0/modules/webdav/vendor/Sabre/autoload.php From 470d2b94668ecf6ebb6f62d1fb605cb2ea960664 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 17 Dec 2010 14:46:14 -0800 Subject: [PATCH 143/300] Clean up some small issues, rename the root back to webdav/gallery. Updated for the move from libraries to vendor. --- 3.0/modules/webdav/controllers/webdav.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php index 56bc388d..d7f2082e 100644 --- a/3.0/modules/webdav/controllers/webdav.php +++ b/3.0/modules/webdav/controllers/webdav.php @@ -17,10 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -require_once(MODPATH . "webdav/libraries/Sabre/autoload.php"); +require_once(MODPATH . "webdav/vendor/Sabre/autoload.php"); class WebDAV_Controller extends Controller { - public function index() { + public function gallery() { $root = new Gallery3_Album(""); $tree = new Gallery3_DAV_Tree($root); @@ -30,7 +30,7 @@ class WebDAV_Controller extends Controller { $filter = new Sabre_DAV_TemporaryFileFilterPlugin(TMPPATH . "sabredav"); $server = new Sabre_DAV_Server($tree); - $server->setBaseUri(url::site("/")); + $server->setBaseUri(url::site("webdav/gallery")); // $server->addPlugin($lock); $server->addPlugin($filter); @@ -122,15 +122,15 @@ class Gallery3_DAV_Tree extends Sabre_DAV_Tree { $target_item = $this->cache->to_album($target); try { - access::required("view", $sourceItem); - access::required("edit", $sourceItem); - access::required("view", $targetItem); - access::required("edit", $targetItem); + access::required("view", $source_item); + access::required("edit", $source_item); + access::required("view", $target_item); + access::required("edit", $target_item); } catch (Kohana_404_Exception $e) { throw new Sabre_DAV_Exception_Forbidden("Access denied"); } - $source_item->parent_id = $targetItem->id; + $source_item->parent_id = $target_item->id; $source_item->save(); return true; } From ef00c9515647430c3997e2f16c3e8a8704255b1e Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 17 Dec 2010 14:52:30 -0800 Subject: [PATCH 144/300] Remove empty helpers --- 3.0/modules/webdav/helpers/webdav.php | 26 --------------------- 3.0/modules/webdav/helpers/webdav_event.php | 23 ------------------ 2 files changed, 49 deletions(-) delete mode 100644 3.0/modules/webdav/helpers/webdav.php delete mode 100644 3.0/modules/webdav/helpers/webdav_event.php diff --git a/3.0/modules/webdav/helpers/webdav.php b/3.0/modules/webdav/helpers/webdav.php deleted file mode 100644 index fa7c3fb2..00000000 --- a/3.0/modules/webdav/helpers/webdav.php +++ /dev/null @@ -1,26 +0,0 @@ - Date: Fri, 17 Dec 2010 14:52:59 -0800 Subject: [PATCH 145/300] Remove copy/pasted file --- 3.0/modules/webdav/views/author_block.html.php | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 3.0/modules/webdav/views/author_block.html.php diff --git a/3.0/modules/webdav/views/author_block.html.php b/3.0/modules/webdav/views/author_block.html.php deleted file mode 100644 index bdcf2e6d..00000000 --- a/3.0/modules/webdav/views/author_block.html.php +++ /dev/null @@ -1,6 +0,0 @@ - - -
                        -: -
                        - From dc2c2e5471356a938caab35cc2fbe1e5364da694 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 17 Dec 2010 14:53:11 -0800 Subject: [PATCH 146/300] Remove copy/pasted file --- 3.0/modules/webdav/models/author_record.php | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 3.0/modules/webdav/models/author_record.php diff --git a/3.0/modules/webdav/models/author_record.php b/3.0/modules/webdav/models/author_record.php deleted file mode 100644 index 80c59d22..00000000 --- a/3.0/modules/webdav/models/author_record.php +++ /dev/null @@ -1,21 +0,0 @@ - Date: Fri, 17 Dec 2010 14:53:33 -0800 Subject: [PATCH 147/300] Refactor the DAV components out into the libraries dir. --- 3.0/modules/webdav/controllers/webdav.php | 261 +----------------- .../webdav/libraries/Gallery3_DAV_Album.php | 128 +++++++++ .../webdav/libraries/Gallery3_DAV_Cache.php | 66 +++++ .../webdav/libraries/Gallery3_DAV_File.php | 78 ++++++ .../webdav/libraries/Gallery3_DAV_Tree.php | 60 ++++ 5 files changed, 333 insertions(+), 260 deletions(-) create mode 100644 3.0/modules/webdav/libraries/Gallery3_DAV_Album.php create mode 100644 3.0/modules/webdav/libraries/Gallery3_DAV_Cache.php create mode 100644 3.0/modules/webdav/libraries/Gallery3_DAV_File.php create mode 100644 3.0/modules/webdav/libraries/Gallery3_DAV_Tree.php diff --git a/3.0/modules/webdav/controllers/webdav.php b/3.0/modules/webdav/controllers/webdav.php index d7f2082e..ae695cc6 100644 --- a/3.0/modules/webdav/controllers/webdav.php +++ b/3.0/modules/webdav/controllers/webdav.php @@ -21,7 +21,7 @@ require_once(MODPATH . "webdav/vendor/Sabre/autoload.php"); class WebDAV_Controller extends Controller { public function gallery() { - $root = new Gallery3_Album(""); + $root = new Gallery3_DAV_Album(""); $tree = new Gallery3_DAV_Tree($root); // Skip the lock plugin for now, we don't want Finder to get write support for the time being. @@ -61,262 +61,3 @@ class WebDAV_Controller extends Controller { } } -class Gallery3_DAV_Cache { - private static $cache; - private static $instance; - - private function __construct() { - self::$cache = array(); - } - - public static function instance() { - if (!isset(self::$instance)) { - self::$instance = new Gallery3_DAV_Cache(); - } - return self::$instance; - } - - private function encode_path($path) { - $path = trim($path, "/"); - $encoded_array = array(); - foreach (explode("/", $path) as $part) { - $encoded_array[] = rawurlencode($part); - } - - return join("/", $encoded_array); - } - - public function to_album($path) { - $path = substr($path, 0, strrpos($path, "/")); - return $this->to_item($path); - } - - public function to_item($path) { - $path = trim($path, "/"); - $path = $this->encode_path($path); - - if (!isset(self::$cache[$path])) { - self::$cache[$path] = ORM::factory("item") - ->viewable() - ->where("relative_path_cache", "=", $path) - ->find(); - } - - return self::$cache[$path]; - } - - public function __clone() { - } -} - -class Gallery3_DAV_Tree extends Sabre_DAV_Tree { - protected $root_node; - - public function __construct(Sabre_DAV_ICollection $root_node) { - $this->cache = Gallery3_DAV_Cache::instance(); - $this->root_node = $root_node; - } - - public function move($source, $target) { - $source_item = $this->cache->to_item($source); - $target_item = $this->cache->to_album($target); - - try { - access::required("view", $source_item); - access::required("edit", $source_item); - access::required("view", $target_item); - access::required("edit", $target_item); - } catch (Kohana_404_Exception $e) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - } - - $source_item->parent_id = $target_item->id; - $source_item->save(); - return true; - } - - public function getNodeForPath($path) { - $path = trim($path,"/"); - $item = $this->cache->to_item($path); - - if (!$item->loaded()) { - throw new Sabre_DAV_Exception_FileNotFound("Could not find node at path: $path"); - } - - if ($item->is_album()) { - return new Gallery3_Album($path); - } else { - return new Gallery3_File($path); - } - } -} - -class Gallery3_Album extends Sabre_DAV_Directory { - private $item; - private $stat; - private $path; - - function __construct($path) { - $this->cache = Gallery3_DAV_Cache::instance(); - $this->path = $path; - $this->item = $this->cache->to_item($path); - } - - function getName() { - return $this->item->name; - } - - function getChildren() { - $return = array(); - foreach ($this->item->viewable()->children() as $child) { - $return[] = $this->getChild($child->name); - } - return $return; - } - - function getChild($name) { - $rp = "{$this->path}/$name"; - $child = $this->cache->to_item($rp); - - if (!access::can("view", $child)) { - throw new Sabre_DAV_Exception_FileNotFound("Access denied"); - } - - if ($child->is_album()) { - return new Gallery3_Album($rp); - } else { - return new Gallery3_File($rp); - } - } - - public function createFile($name, $data=null) { - try { - access::required("view", $this->item); - access::required("add", $this->item); - } catch (Kohana_404_Exception $e) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - } - if (substr($name, 0, 1) == ".") { - return true; - }; - - try { - $tempfile = tempnam(TMPPATH, "dav"); - $target = fopen($tempfile, "wb"); - stream_copy_to_stream($data, $target); - fclose($target); - - $item = ORM::factory("item"); - $item->name = $name; - $item->title = item::convert_filename_to_title($item->name); - $item->description = ""; - $item->parent_id = $this->item->id; - $item->set_data_file($tempfile); - $item->type = "photo"; - $item->save(); - } catch (Exception $e) { - unlink($tempfile); - throw $e; - } - } - - public function createDirectory($name) { - try { - access::required("view", $this->item); - access::required("add", $this->item); - } catch (Kohana_404_Exception $e) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - } - - $album = ORM::factory("item"); - $album->type = "album"; - $album->parent_id = $this->item->id; - $album->name = $name; - $album->title = $name; - $album->description = ""; - $album->save(); - - // Refresh MPTT pointers - $this->item->reload(); - } - - function getLastModified() { - return $this->item->updated; - } - - function setName($name) { - if (!access::can("edit", $this->item)) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - }; - - $this->item->name = $name; - $this->item->save(); - } - - public function delete() { - if (!access::can("edit", $this->item)) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - }; - $this->item->delete(); - } -} - -class Gallery3_File extends Sabre_DAV_File { - private $item; - private $stat; - private $path; - - function __construct($path) { - $this->cache = Gallery3_DAV_Cache::instance(); - $this->item = $this->cache->to_item($path); - - if (access::can("view_full", $this->item)) { - $this->stat = stat($this->item->file_path()); - $this->path = $this->item->file_path(); - } else { - $this->stat = stat($this->item->resize_path()); - $this->path = $this->item->resize_path(); - } - } - - public function delete() { - if (!access::can("edit", $this->item)) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - }; - $this->item->delete(); - } - - function setName($name) { - if (!access::can("edit", $this->item)) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - }; - $this->item->name = $name; - $this->item->save(); - } - - public function getLastModified() { - return $this->item->updated; - } - - function get() { - if (!access::can("view", $this->item)) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - }; - return fopen($this->path, "r"); - } - - function getSize() { - return $this->stat[7]; - } - - function getName() { - return $this->item->name; - } - - function getETag() { - if (!access::can("view", $this->item)) { - throw new Sabre_DAV_Exception_Forbidden("Access denied"); - }; - return "'" . md5($this->item->file_path()) . "'"; - } -} diff --git a/3.0/modules/webdav/libraries/Gallery3_DAV_Album.php b/3.0/modules/webdav/libraries/Gallery3_DAV_Album.php new file mode 100644 index 00000000..350e37b8 --- /dev/null +++ b/3.0/modules/webdav/libraries/Gallery3_DAV_Album.php @@ -0,0 +1,128 @@ +cache = Gallery3_DAV_Cache::instance(); + $this->path = $path; + $this->item = $this->cache->to_item($path); + } + + function getName() { + return $this->item->name; + } + + function getChildren() { + $return = array(); + foreach ($this->item->viewable()->children() as $child) { + $return[] = $this->getChild($child->name); + } + return $return; + } + + function getChild($name) { + $rp = "{$this->path}/$name"; + $child = $this->cache->to_item($rp); + + if (!access::can("view", $child)) { + throw new Sabre_DAV_Exception_FileNotFound("Access denied"); + } + + if ($child->is_album()) { + return new Gallery3_DAV_Album($rp); + } else { + return new Gallery3_DAV_File($rp); + } + } + + public function createFile($name, $data=null) { + try { + access::required("view", $this->item); + access::required("add", $this->item); + } catch (Kohana_404_Exception $e) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + } + if (substr($name, 0, 1) == ".") { + return true; + }; + + try { + $tempfile = tempnam(TMPPATH, "dav"); + $target = fopen($tempfile, "wb"); + stream_copy_to_stream($data, $target); + fclose($target); + + $item = ORM::factory("item"); + $item->name = $name; + $item->title = item::convert_filename_to_title($item->name); + $item->description = ""; + $item->parent_id = $this->item->id; + $item->set_data_file($tempfile); + $item->type = "photo"; + $item->save(); + } catch (Exception $e) { + unlink($tempfile); + throw $e; + } + } + + public function createDirectory($name) { + try { + access::required("view", $this->item); + access::required("add", $this->item); + } catch (Kohana_404_Exception $e) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + } + + $album = ORM::factory("item"); + $album->type = "album"; + $album->parent_id = $this->item->id; + $album->name = $name; + $album->title = $name; + $album->description = ""; + $album->save(); + + // Refresh MPTT pointers + $this->item->reload(); + } + + function getLastModified() { + return $this->item->updated; + } + + function setName($name) { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + + $this->item->name = $name; + $this->item->save(); + } + + public function delete() { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + $this->item->delete(); + } +} diff --git a/3.0/modules/webdav/libraries/Gallery3_DAV_Cache.php b/3.0/modules/webdav/libraries/Gallery3_DAV_Cache.php new file mode 100644 index 00000000..998bd52f --- /dev/null +++ b/3.0/modules/webdav/libraries/Gallery3_DAV_Cache.php @@ -0,0 +1,66 @@ +to_item($path); + } + + public function to_item($path) { + $path = trim($path, "/"); + $path = $this->encode_path($path); + + if (!isset(self::$cache[$path])) { + self::$cache[$path] = ORM::factory("item") + ->viewable() + ->where("relative_path_cache", "=", $path) + ->find(); + } + + return self::$cache[$path]; + } + + public function __clone() { + } +} diff --git a/3.0/modules/webdav/libraries/Gallery3_DAV_File.php b/3.0/modules/webdav/libraries/Gallery3_DAV_File.php new file mode 100644 index 00000000..2f41b86f --- /dev/null +++ b/3.0/modules/webdav/libraries/Gallery3_DAV_File.php @@ -0,0 +1,78 @@ +cache = Gallery3_DAV_Cache::instance(); + $this->item = $this->cache->to_item($path); + + if (access::can("view_full", $this->item)) { + $this->stat = stat($this->item->file_path()); + $this->path = $this->item->file_path(); + } else { + $this->stat = stat($this->item->resize_path()); + $this->path = $this->item->resize_path(); + } + } + + public function delete() { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + $this->item->delete(); + } + + function setName($name) { + if (!access::can("edit", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + $this->item->name = $name; + $this->item->save(); + } + + public function getLastModified() { + return $this->item->updated; + } + + function get() { + if (!access::can("view", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + return fopen($this->path, "r"); + } + + function getSize() { + return $this->stat[7]; + } + + function getName() { + return $this->item->name; + } + + function getETag() { + if (!access::can("view", $this->item)) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + }; + return "'" . md5($this->item->file_path()) . "'"; + } +} diff --git a/3.0/modules/webdav/libraries/Gallery3_DAV_Tree.php b/3.0/modules/webdav/libraries/Gallery3_DAV_Tree.php new file mode 100644 index 00000000..6e31e005 --- /dev/null +++ b/3.0/modules/webdav/libraries/Gallery3_DAV_Tree.php @@ -0,0 +1,60 @@ +cache = Gallery3_DAV_Cache::instance(); + $this->root_node = $root_node; + } + + public function move($source, $target) { + $source_item = $this->cache->to_item($source); + $target_item = $this->cache->to_album($target); + + try { + access::required("view", $source_item); + access::required("edit", $source_item); + access::required("view", $target_item); + access::required("edit", $target_item); + } catch (Kohana_404_Exception $e) { + throw new Sabre_DAV_Exception_Forbidden("Access denied"); + } + + $source_item->parent_id = $target_item->id; + $source_item->save(); + return true; + } + + public function getNodeForPath($path) { + $path = trim($path,"/"); + $item = $this->cache->to_item($path); + + if (!$item->loaded()) { + throw new Sabre_DAV_Exception_FileNotFound("Could not find node at path: $path"); + } + + if ($item->is_album()) { + return new Gallery3_DAV_Album($path); + } else { + return new Gallery3_DAV_File($path); + } + } +} From 091357a9d209d8d7e86a152d727a593d4397e347 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Fri, 17 Dec 2010 17:08:19 -0800 Subject: [PATCH 148/300] Add a second style where we use unordered lists instead of a . It's hacky, but works. Switch between styles using Admin > Settings > Advanced and changing albumtree.style between "select" and "list". --- .../albumtree/helpers/albumtree_block.php | 3 +- .../albumtree/helpers/albumtree_installer.php | 33 ++++++++++++++++ 3.1/modules/albumtree/module.info | 2 +- .../views/albumtree_block_list.html.php | 38 +++++++++++++++++++ .../views/albumtree_block_select.html.php | 24 ++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 3.1/modules/albumtree/helpers/albumtree_installer.php create mode 100644 3.1/modules/albumtree/views/albumtree_block_list.html.php create mode 100644 3.1/modules/albumtree/views/albumtree_block_select.html.php diff --git a/3.1/modules/albumtree/helpers/albumtree_block.php b/3.1/modules/albumtree/helpers/albumtree_block.php index 8d184f51..1d01e4fd 100644 --- a/3.1/modules/albumtree/helpers/albumtree_block.php +++ b/3.1/modules/albumtree/helpers/albumtree_block.php @@ -26,9 +26,10 @@ class albumtree_block_Core { $block = new Block(); switch ($block_id) { case "albumtree": + $style = module::get_var("albumtree", "style", "select"); $block->css_id = "g-albumtree"; $block->title = t("Album Tree"); - $block->content = new View("albumtree_block.html"); + $block->content = new View("albumtree_block_{$style}.html"); $block->content->root = item::root(); break; } diff --git a/3.1/modules/albumtree/helpers/albumtree_installer.php b/3.1/modules/albumtree/helpers/albumtree_installer.php new file mode 100644 index 00000000..30ceb884 --- /dev/null +++ b/3.1/modules/albumtree/helpers/albumtree_installer.php @@ -0,0 +1,33 @@ + + + + +
                      + diff --git a/3.1/modules/albumtree/views/albumtree_block_select.html.php b/3.1/modules/albumtree/views/albumtree_block_select.html.php new file mode 100644 index 00000000..4a73c333 --- /dev/null +++ b/3.1/modules/albumtree/views/albumtree_block_select.html.php @@ -0,0 +1,24 @@ + + From 55babdfc8514e651cee14e673074eb45a77eacbb Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sat, 18 Dec 2010 15:06:08 +0100 Subject: [PATCH 151/300] FIX: Directory separator was not properly handled on Windows servers --- 3.0/modules/downloadalbum/controllers/downloadalbum.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/3.0/modules/downloadalbum/controllers/downloadalbum.php b/3.0/modules/downloadalbum/controllers/downloadalbum.php index 73ebd579..79df86ff 100644 --- a/3.0/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.0/modules/downloadalbum/controllers/downloadalbum.php @@ -174,7 +174,8 @@ class downloadalbum_Controller extends Controller { continue; } - $i_relative_path = str_replace($container_realpath.'/', '', $i_realpath); + $i_relative_path = str_replace($container_realpath.DIRECTORY_SEPARATOR, '', $i_realpath); + $i_relative_path = str_replace(DIRECTORY_SEPARATOR, '/', $i_relative_path); $files[$i_relative_path] = $i_realpath; } From 8aa45472a807c42a2949d77786932e3b36c7431a Mon Sep 17 00:00:00 2001 From: Romain LE DISEZ Date: Sat, 18 Dec 2010 15:07:26 +0100 Subject: [PATCH 152/300] Sync downloadalbum in branch '3.1' --- .../controllers/downloadalbum.php | 146 +++++++++++------- .../helpers/downloadalbum_event.php | 26 ++-- .../helpers/downloadalbum_theme.php | 4 +- 3.1/modules/downloadalbum/module.info | 2 +- 4 files changed, 106 insertions(+), 72 deletions(-) diff --git a/3.1/modules/downloadalbum/controllers/downloadalbum.php b/3.1/modules/downloadalbum/controllers/downloadalbum.php index c2f939f7..79df86ff 100644 --- a/3.1/modules/downloadalbum/controllers/downloadalbum.php +++ b/3.1/modules/downloadalbum/controllers/downloadalbum.php @@ -22,30 +22,55 @@ class downloadalbum_Controller extends Controller { /** * Generate a ZIP on-the-fly. */ - public function zip($id) { - $album = $this->init($id); - $files = $this->getFilesList($album); + public function zip($container_type, $id) { + switch($container_type) { + case "album": + $container = ORM::factory("item", $id); + if (!$container->is_album()) { + throw new Kohana_Exception('container is not an album: '.$container->relative_path()); + } + + $zipname = (empty($container->name)) + ? 'Gallery.zip' // @todo purified_version_of($container->title).'.zip' + : $container->name.'.zip'; + break; + + case "tag": + // @todo: if the module is not installed, it crash + $container = ORM::factory("tag", $id); + if (is_null($container->name)) { + throw new Kohana_Exception('container is not a tag: '.$id); + } + + $zipname = $container->name.'.zip'; + break; + + default: + throw new Kohana_Exception('unhandled container type: '.$container_type); + } + + $files = $this->getFilesList($container); // Calculate ZIP size (look behind for details) $zipsize = 22; - foreach($files as $f) { - $zipsize += 76 + 2*strlen($f) + filesize($f); + foreach($files as $f_name => $f_path) { + $zipsize += 76 + 2*strlen($f_name) + filesize($f_path); } // Send headers $this->prepareOutput(); - $this->sendHeaders($album->name.'.zip', $zipsize); + $this->sendHeaders($zipname, $zipsize); // Generate and send ZIP file // http://www.pkware.com/documents/casestudies/APPNOTE.TXT (v6.3.2) $lfh_offset = 0; $cds = ''; $cds_offset = 0; - foreach($files as $f) { - $f_namelen = strlen($f); - $f_size = filesize($f); - $f_mtime = $this->unix2dostime(filemtime($f)); - $f_crc32 = $this->fixBug45028(hexdec(hash_file('crc32b', $f, false))); + foreach($files as $f_name => $f_path) { + $f_namelen = strlen($f_name); + $f_size = filesize($f_path); + $f_mtime = $this->unix2dostime(filemtime($f_path)); + $f_crc32 = $this->fixBug45028(hexdec(hash_file('crc32b', $f_path, false))); // Local file header echo pack('VvvvVVVVvva' . $f_namelen, @@ -60,12 +85,12 @@ class downloadalbum_Controller extends Controller { $f_namelen, // file name length (2 bytes) 0, // extra field length (2 bytes) - $f // file name (variable size) + $f_name // file name (variable size) // extra field (variable size) => n/a ); // File data - readfile($f); + readfile($f_path); // Data descriptor (n/a) @@ -88,7 +113,7 @@ class downloadalbum_Controller extends Controller { 0x81b40000, // external file attributes (4 bytes) => chmod 664 $lfh_offset, // relative offset of local header (4 bytes) - $f // file name (variable size) + $f_name // file name (variable size) // extra field (variable size) => n/a // file comment (variable size) => n/a ); @@ -128,59 +153,58 @@ class downloadalbum_Controller extends Controller { } - /** - * Init - */ - private function init($id) { - $item = ORM::factory("item", $id); - - // Only send an album - if (!$item->is_album()) { - // @todo: throw an exception? - Kohana::log('error', 'item is not an album: '.$item->relative_path()); - exit; - } - - // Must have view_full to download the originals files - access::required("view_full", $item); - - return $item; - } - /** * Return the files that must be included in the archive. */ - private function getFilesList($album) { + private function getFilesList($container) { $files = array(); - // Go to the parent of album so the ZIP will not contains all the - // server hierarchy - if (!chdir($album->file_path().'/../')) { - // @todo: throw an exception? - Kohana::log('error', 'unable to chdir('.$item->file_path().'/../)'); - exit; - } - $cwd = getcwd(); + if( $container instanceof Item_Model && $container->is_album() ) { + $container_realpath = realpath($container->file_path().'/../'); - $items = $album->viewable() - ->descendants(null, null, array(array("type", "<>", "album"))); - foreach($items as $i) { - if (!access::can('view_full', $i)) { - continue; + $items = $container->viewable() + ->descendants(null, null, array(array("type", "<>", "album"))); + foreach($items as $i) { + if (!access::can('view_full', $i)) { + continue; + } + + $i_realpath = realpath($i->file_path()); + if (!is_readable($i_realpath)) { + continue; + } + + $i_relative_path = str_replace($container_realpath.DIRECTORY_SEPARATOR, '', $i_realpath); + $i_relative_path = str_replace(DIRECTORY_SEPARATOR, '/', $i_relative_path); + $files[$i_relative_path] = $i_realpath; } - $relative_path = str_replace($cwd.'/', '', realpath($i->file_path())); - if (!is_readable($relative_path)) { - continue; - } + } else if( $container instanceof Tag_Model ) { + $items = $container->items(); + foreach($items as $i) { + if (!access::can('view_full', $i)) { + continue; + } - $files[] = $relative_path; + if( $i->is_album() ) { + foreach($this->getFilesList($i) as $f_name => $f_path) { + $files[$container->name.'/'.$f_name] = $f_path; + } + + } else { + $i_realpath = realpath($i->file_path()); + if (!is_readable($i_realpath)) { + continue; + } + + $i_relative_path = $container->name.'/'.$i->name; + $files[$i_relative_path] = $i_realpath; + } + } } if (count($files) === 0) { - // @todo: throw an exception? - Kohana::log('error', 'no zippable files in ['.$album->relative_path().']'); - exit; + throw new Kohana_Exception('no zippable files in ['.$container->name.']'); } return $files; @@ -264,9 +288,13 @@ class downloadalbum_Controller extends Controller { * See http://bugs.php.net/bug.php?id=45028 */ private function fixBug45028($hash) { - return (version_compare(PHP_VERSION, '5.2.7', '<')) - ? (($hash & 0x000000ff) << 24) + (($hash & 0x0000ff00) << 8) - + (($hash & 0x00ff0000) >> 8) + (($hash & 0xff000000) >> 24) - : $hash; + $output = $hash; + + if( version_compare(PHP_VERSION, '5.2.7', '<') ) { + $str = str_pad(dechex($hash), 8, '0', STR_PAD_LEFT); + $output = hexdec($str{6}.$str{7}.$str{4}.$str{5}.$str{2}.$str{3}.$str{0}.$str{1}); + } + + return $output; } } diff --git a/3.1/modules/downloadalbum/helpers/downloadalbum_event.php b/3.1/modules/downloadalbum/helpers/downloadalbum_event.php index e2fe4420..e58512ac 100644 --- a/3.1/modules/downloadalbum/helpers/downloadalbum_event.php +++ b/3.1/modules/downloadalbum/helpers/downloadalbum_event.php @@ -19,14 +19,22 @@ */ class downloadalbum_event_Core { static function album_menu($menu, $theme) { - if (access::can("view_full", $theme->item)) { - $downloadLink = url::site("downloadalbum/zip/{$theme->item->id}"); - $menu - ->append(Menu::factory("link") - ->id("downloadalbum") - ->label(t("Download Album")) - ->url($downloadLink) - ->css_id("g-download-album-link")); - } + $downloadLink = url::site("downloadalbum/zip/album/{$theme->item->id}"); + $menu + ->append(Menu::factory("link") + ->id("downloadalbum") + ->label(t("Download Album")) + ->url($downloadLink) + ->css_id("g-download-album-link")); } + + static function tag_menu($menu, $theme) { + $downloadLink = url::site("downloadalbum/zip/tag/{$theme->tag()->id}"); + $menu + ->append(Menu::factory("link") + ->id("downloadalbum") + ->label(t("Download Album")) + ->url($downloadLink) + ->css_id("g-download-album-link")); + } } diff --git a/3.1/modules/downloadalbum/helpers/downloadalbum_theme.php b/3.1/modules/downloadalbum/helpers/downloadalbum_theme.php index 2fd23552..d88d8a52 100644 --- a/3.1/modules/downloadalbum/helpers/downloadalbum_theme.php +++ b/3.1/modules/downloadalbum/helpers/downloadalbum_theme.php @@ -19,8 +19,6 @@ */ class downloadalbum_theme { static function head($theme) { - if ($theme->item && access::can("view_full", $theme->item)) { - $theme->css("downloadalbum_menu.css"); - } + $theme->css("downloadalbum_menu.css"); } } diff --git a/3.1/modules/downloadalbum/module.info b/3.1/modules/downloadalbum/module.info index 177ad4a8..11c52ba2 100644 --- a/3.1/modules/downloadalbum/module.info +++ b/3.1/modules/downloadalbum/module.info @@ -1,3 +1,3 @@ name = "DownloadAlbum" description = "Displays a link to download a ZIP archive of the current album." -version = 1 +version = 2 From 0eb7d3d950fed405ea9a30509b91d6440c12180d Mon Sep 17 00:00:00 2001 From: sykong Date: Sun, 19 Dec 2010 04:59:50 -0800 Subject: [PATCH 153/300] Modified to handle large albums. Now, the module will only tag 50 at a time and continue automatically. If it is still too much, and it "stops", there will also be a "continue" link to allow resuming. The continue link will also adjust the maximum number of tags at a time (from the default 50) to the current number where batchtag "stops". --- 3.0/modules/batchtag/controllers/batchtag.php | 85 +++++++++++++++---- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/3.0/modules/batchtag/controllers/batchtag.php b/3.0/modules/batchtag/controllers/batchtag.php index ec012719..bbfb3ea1 100644 --- a/3.0/modules/batchtag/controllers/batchtag.php +++ b/3.0/modules/batchtag/controllers/batchtag.php @@ -25,42 +25,93 @@ class BatchTag_Controller extends Controller { access::verify_csrf(); $input = Input::instance(); + url::redirect(url::abs_site("batchtag/tagitems2?name={$input->post('name')}&item_id={$input->post('item_id')}&tag_subitems={$input->post('tag_subitems')}&csrf={$input->post('csrf')}")); + } + + public function tagitems2() { + // Tag all non-album items in the current album with the specified tags. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + $input = Input::instance(); + + // Variables + if (($input->get("batchtag_max") == false) || ($input->get("batchtag_max") == "0")) { + $batchtag_max = "50"; + } else { + $batchtag_max = $input->get("batchtag_max"); + } + if ($input->get("batchtag_items_processed") == false) { + $batchtag_items_processed = "0"; + } else { + $batchtag_items_processed = $input->get("batchtag_items_processed"); + } + // Figure out if the contents of sub-albums should also be tagged - $str_tag_subitems = $input->post("tag_subitems"); + $str_tag_subitems = $input->get("tag_subitems"); $children = ""; if ($str_tag_subitems == false) { // Generate an array of all non-album items in the current album. $children = ORM::factory("item") - ->where("parent_id", "=", $input->post("item_id")) + ->where("parent_id", "=", $input->get("item_id")) ->where("type", "!=", "album") ->find_all(); } else { // Generate an array of all non-album items in the current album // and any sub albums. - $item = ORM::factory("item", $input->post("item_id")); + $item = ORM::factory("item", $input->get("item_id")); $children = $item->descendants(); } + // Loop through each item in the album and make sure the user has // access to view and edit it. - foreach ($children as $child) { - if (access::can("view", $child) && access::can("edit", $child) && !$child->is_album()) { + $children_count = "0"; + $tag_count = "0"; - // Assuming the user can view/edit the current item, loop - // through each tag that was submitted and apply it to - // the current item. - foreach (explode(",", $input->post("name")) as $tag_name) { - $tag_name = trim($tag_name); - if ($tag_name) { - tag::add($child, $tag_name); + //echo Kohana::debug($children); + + echo ''; + + foreach ($children as $child) { + + if ($tag_count < $batchtag_max) { + + if ($children_count >= $batchtag_items_processed) { + if (access::can("view", $child) && access::can("edit", $child) && !$child->is_album()) { + + // Assuming the user can view/edit the current item, loop + // through each tag that was submitted and apply it to + // the current item. + foreach (explode(",", $input->get("name")) as $tag_name) { + $tag_name = trim($tag_name); + if ($tag_name) { + tag::add($child, $tag_name); + } + // $tag_count should be inside the foreach loop as it is depending on the number of time tag:add is run + $tag_count++; + } } - } - } + echo '' . "\n"; + $children_count++; + $batchtag_max_new = $tag_count; + echo ''; + } else { $children_count++; } + + } else { break; } + } - // Redirect back to the album. - $item = ORM::factory("item", $input->post("item_id")); - url::redirect(url::abs_site("{$item->type}s/{$item->id}")); + if ($tag_count < $batchtag_max) { + // Redirect back to the album. + $item = ORM::factory("item", $input->get("item_id")); + url::redirect(url::abs_site("{$item->type}s/{$item->id}")); + //echo url::abs_site("{$item->type}s/{$item->id}"); + } else { + url::redirect(url::abs_site("batchtag/tagitems2?name={$input->get('name')}&item_id={$input->get('item_id')}&tag_subitems={$input->get('tag_subitems')}&batchtag_items_processed=$children_count&batchtag_max=$batchtag_max&csrf={$input->get('csrf')}")); + //echo url::abs_site("batchtag/tagitems2?name={$input->get('name')}&item_id={$input->get('item_id')}&tag_subitems={$input->get('tag_subitems')}&batchtag_items_processed=$children_count&batchtag_max=$batchtag_max&csrf={$input->get('csrf')}"); + } } } From 7b336e880445a7d4a0461b18fcee032d8dd1c8da Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sun, 19 Dec 2010 17:41:38 -0800 Subject: [PATCH 154/300] New "About this photo" module that puts info in the sidebar about when the photo was created and what tags it has. Written for undagiga in http://gallery.menalto.com/node/99716 --- .../helpers/about_this_photo_block.php | 52 +++++++++++++++++++ 3.0/modules/about_this_photo/module.info | 3 ++ .../views/about_this_photo.html.php | 17 ++++++ .../helpers/about_this_photo_block.php | 52 +++++++++++++++++++ 3.1/modules/about_this_photo/module.info | 3 ++ .../views/about_this_photo.html.php | 17 ++++++ 6 files changed, 144 insertions(+) create mode 100644 3.0/modules/about_this_photo/helpers/about_this_photo_block.php create mode 100644 3.0/modules/about_this_photo/module.info create mode 100644 3.0/modules/about_this_photo/views/about_this_photo.html.php create mode 100644 3.1/modules/about_this_photo/helpers/about_this_photo_block.php create mode 100644 3.1/modules/about_this_photo/module.info create mode 100644 3.1/modules/about_this_photo/views/about_this_photo.html.php diff --git a/3.0/modules/about_this_photo/helpers/about_this_photo_block.php b/3.0/modules/about_this_photo/helpers/about_this_photo_block.php new file mode 100644 index 00000000..c1adb174 --- /dev/null +++ b/3.0/modules/about_this_photo/helpers/about_this_photo_block.php @@ -0,0 +1,52 @@ + t("About This Photo")); + } + + static function get($block_id, $theme) { + $block = new Block(); + switch ($block_id) { + case "simple": + $block->css_id = "g-about-this-photo"; + $block->title = t("About this photo"); + $block->content = new View("about_this_photo.html"); + + // exif API doesn't give easy access to individual keys, so do this the hard way + if (module::is_active("exif")) { + $exif = ORM::factory("exif_record")->where("item_id", "=", $theme->item()->id)->find(); + if ($exif->loaded()) { + $exif = unserialize($exif->data); + $timestamp = strtotime($exif["DateTime"]); + $block->content->date = gallery::date($timestamp); + $block->content->time = gallery::time($timestamp); + } + } + + if (module::is_active("tag")) { + $block->content->tags = tag::item_tags($theme->item()); + } + break; + } + return $block; + } +} \ No newline at end of file diff --git a/3.0/modules/about_this_photo/module.info b/3.0/modules/about_this_photo/module.info new file mode 100644 index 00000000..6943685e --- /dev/null +++ b/3.0/modules/about_this_photo/module.info @@ -0,0 +1,3 @@ +name = "About this Photo" +description = "Show some simple, specific and useful info about a given photo" +version = 1 diff --git a/3.0/modules/about_this_photo/views/about_this_photo.html.php b/3.0/modules/about_this_photo/views/about_this_photo.html.php new file mode 100644 index 00000000..aa91c578 --- /dev/null +++ b/3.0/modules/about_this_photo/views/about_this_photo.html.php @@ -0,0 +1,17 @@ + + diff --git a/3.1/modules/about_this_photo/helpers/about_this_photo_block.php b/3.1/modules/about_this_photo/helpers/about_this_photo_block.php new file mode 100644 index 00000000..c1adb174 --- /dev/null +++ b/3.1/modules/about_this_photo/helpers/about_this_photo_block.php @@ -0,0 +1,52 @@ + t("About This Photo")); + } + + static function get($block_id, $theme) { + $block = new Block(); + switch ($block_id) { + case "simple": + $block->css_id = "g-about-this-photo"; + $block->title = t("About this photo"); + $block->content = new View("about_this_photo.html"); + + // exif API doesn't give easy access to individual keys, so do this the hard way + if (module::is_active("exif")) { + $exif = ORM::factory("exif_record")->where("item_id", "=", $theme->item()->id)->find(); + if ($exif->loaded()) { + $exif = unserialize($exif->data); + $timestamp = strtotime($exif["DateTime"]); + $block->content->date = gallery::date($timestamp); + $block->content->time = gallery::time($timestamp); + } + } + + if (module::is_active("tag")) { + $block->content->tags = tag::item_tags($theme->item()); + } + break; + } + return $block; + } +} \ No newline at end of file diff --git a/3.1/modules/about_this_photo/module.info b/3.1/modules/about_this_photo/module.info new file mode 100644 index 00000000..6943685e --- /dev/null +++ b/3.1/modules/about_this_photo/module.info @@ -0,0 +1,3 @@ +name = "About this Photo" +description = "Show some simple, specific and useful info about a given photo" +version = 1 diff --git a/3.1/modules/about_this_photo/views/about_this_photo.html.php b/3.1/modules/about_this_photo/views/about_this_photo.html.php new file mode 100644 index 00000000..aa91c578 --- /dev/null +++ b/3.1/modules/about_this_photo/views/about_this_photo.html.php @@ -0,0 +1,17 @@ + + From 7e3cbfc5760fcb82b83ee3db6017bc2322ef2f7f Mon Sep 17 00:00:00 2001 From: sykong Date: Sun, 19 Dec 2010 20:59:50 +0800 Subject: [PATCH 155/300] Modified to handle large albums. Now, the module will only tag 50 at a time and continue automatically. If it is still too much, and it "stops", there will also be a "continue" link to allow resuming. The continue link will also adjust the maximum number of tags at a time (from the default 50) to the current number where batchtag "stops". --- 3.0/modules/batchtag/controllers/batchtag.php | 85 +++++++++++++++---- 1 file changed, 68 insertions(+), 17 deletions(-) diff --git a/3.0/modules/batchtag/controllers/batchtag.php b/3.0/modules/batchtag/controllers/batchtag.php index ec012719..bbfb3ea1 100644 --- a/3.0/modules/batchtag/controllers/batchtag.php +++ b/3.0/modules/batchtag/controllers/batchtag.php @@ -25,42 +25,93 @@ class BatchTag_Controller extends Controller { access::verify_csrf(); $input = Input::instance(); + url::redirect(url::abs_site("batchtag/tagitems2?name={$input->post('name')}&item_id={$input->post('item_id')}&tag_subitems={$input->post('tag_subitems')}&csrf={$input->post('csrf')}")); + } + + public function tagitems2() { + // Tag all non-album items in the current album with the specified tags. + + // Prevent Cross Site Request Forgery + access::verify_csrf(); + + $input = Input::instance(); + + // Variables + if (($input->get("batchtag_max") == false) || ($input->get("batchtag_max") == "0")) { + $batchtag_max = "50"; + } else { + $batchtag_max = $input->get("batchtag_max"); + } + if ($input->get("batchtag_items_processed") == false) { + $batchtag_items_processed = "0"; + } else { + $batchtag_items_processed = $input->get("batchtag_items_processed"); + } + // Figure out if the contents of sub-albums should also be tagged - $str_tag_subitems = $input->post("tag_subitems"); + $str_tag_subitems = $input->get("tag_subitems"); $children = ""; if ($str_tag_subitems == false) { // Generate an array of all non-album items in the current album. $children = ORM::factory("item") - ->where("parent_id", "=", $input->post("item_id")) + ->where("parent_id", "=", $input->get("item_id")) ->where("type", "!=", "album") ->find_all(); } else { // Generate an array of all non-album items in the current album // and any sub albums. - $item = ORM::factory("item", $input->post("item_id")); + $item = ORM::factory("item", $input->get("item_id")); $children = $item->descendants(); } + // Loop through each item in the album and make sure the user has // access to view and edit it. - foreach ($children as $child) { - if (access::can("view", $child) && access::can("edit", $child) && !$child->is_album()) { + $children_count = "0"; + $tag_count = "0"; - // Assuming the user can view/edit the current item, loop - // through each tag that was submitted and apply it to - // the current item. - foreach (explode(",", $input->post("name")) as $tag_name) { - $tag_name = trim($tag_name); - if ($tag_name) { - tag::add($child, $tag_name); + //echo Kohana::debug($children); + + echo ''; + + foreach ($children as $child) { + + if ($tag_count < $batchtag_max) { + + if ($children_count >= $batchtag_items_processed) { + if (access::can("view", $child) && access::can("edit", $child) && !$child->is_album()) { + + // Assuming the user can view/edit the current item, loop + // through each tag that was submitted and apply it to + // the current item. + foreach (explode(",", $input->get("name")) as $tag_name) { + $tag_name = trim($tag_name); + if ($tag_name) { + tag::add($child, $tag_name); + } + // $tag_count should be inside the foreach loop as it is depending on the number of time tag:add is run + $tag_count++; + } } - } - } + echo '' . "\n"; + $children_count++; + $batchtag_max_new = $tag_count; + echo ''; + } else { $children_count++; } + + } else { break; } + } - // Redirect back to the album. - $item = ORM::factory("item", $input->post("item_id")); - url::redirect(url::abs_site("{$item->type}s/{$item->id}")); + if ($tag_count < $batchtag_max) { + // Redirect back to the album. + $item = ORM::factory("item", $input->get("item_id")); + url::redirect(url::abs_site("{$item->type}s/{$item->id}")); + //echo url::abs_site("{$item->type}s/{$item->id}"); + } else { + url::redirect(url::abs_site("batchtag/tagitems2?name={$input->get('name')}&item_id={$input->get('item_id')}&tag_subitems={$input->get('tag_subitems')}&batchtag_items_processed=$children_count&batchtag_max=$batchtag_max&csrf={$input->get('csrf')}")); + //echo url::abs_site("batchtag/tagitems2?name={$input->get('name')}&item_id={$input->get('item_id')}&tag_subitems={$input->get('tag_subitems')}&batchtag_items_processed=$children_count&batchtag_max=$batchtag_max&csrf={$input->get('csrf')}"); + } } } From 297c91ae3a06c5c954977ce11d9a793a0b5661f4 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sun, 19 Dec 2010 22:42:08 -0800 Subject: [PATCH 156/300] Bump to version 2 and add an installer --- .../user_rest/helpers/user_rest_installer.php | 30 +++++++++++++++++++ 3.0/modules/user_rest/module.info | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 3.0/modules/user_rest/helpers/user_rest_installer.php diff --git a/3.0/modules/user_rest/helpers/user_rest_installer.php b/3.0/modules/user_rest/helpers/user_rest_installer.php new file mode 100644 index 00000000..7701bfb8 --- /dev/null +++ b/3.0/modules/user_rest/helpers/user_rest_installer.php @@ -0,0 +1,30 @@ + Date: Sun, 19 Dec 2010 22:43:50 -0800 Subject: [PATCH 157/300] Bump to version 2 and add an installer --- .../user_rest/helpers/user_rest_installer.php | 30 +++++++++++++++++++ 3.1/modules/user_rest/module.info | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 3.1/modules/user_rest/helpers/user_rest_installer.php diff --git a/3.1/modules/user_rest/helpers/user_rest_installer.php b/3.1/modules/user_rest/helpers/user_rest_installer.php new file mode 100644 index 00000000..7701bfb8 --- /dev/null +++ b/3.1/modules/user_rest/helpers/user_rest_installer.php @@ -0,0 +1,30 @@ + Date: Mon, 20 Dec 2010 14:18:34 -0600 Subject: [PATCH 158/300] Added pylibgal3 submodule --- .gitmodules | 3 +++ 3.0/client/Python/pylibgal3 | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 3.0/client/Python/pylibgal3 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e6646cbd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "3.0/client/Python/pylibgal3"] + path = 3.0/client/Python/pylibgal3 + url = git://github.com/crustymonkey/pylibgal3.git diff --git a/3.0/client/Python/pylibgal3 b/3.0/client/Python/pylibgal3 new file mode 160000 index 00000000..233b5802 --- /dev/null +++ b/3.0/client/Python/pylibgal3 @@ -0,0 +1 @@ +Subproject commit 233b5802cd6f2c466bab74fd82bacf6b0cdef681 From 2a80225313b628797566b66016fea5e0aa54ef4f Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Tue, 21 Dec 2010 11:42:39 -0600 Subject: [PATCH 159/300] Removed the submodule pylibgal3 --- .gitmodules | 3 --- 3.0/client/Python/pylibgal3 | 1 - 2 files changed, 4 deletions(-) delete mode 100644 .gitmodules delete mode 160000 3.0/client/Python/pylibgal3 diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e6646cbd..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "3.0/client/Python/pylibgal3"] - path = 3.0/client/Python/pylibgal3 - url = git://github.com/crustymonkey/pylibgal3.git diff --git a/3.0/client/Python/pylibgal3 b/3.0/client/Python/pylibgal3 deleted file mode 160000 index 233b5802..00000000 --- a/3.0/client/Python/pylibgal3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 233b5802cd6f2c466bab74fd82bacf6b0cdef681 From f1deeecc543e1b8621f7244e9503864d611815e5 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Tue, 21 Dec 2010 11:45:11 -0600 Subject: [PATCH 160/300] Adding the pylibgal3 source --- 3.0/client/Python/pylibgal3/.gitignore | 4 + 3.0/client/Python/pylibgal3/KNOWN_ISSUES | 13 + 3.0/client/Python/pylibgal3/LICENSE | 674 ++++++++++++++++++ 3.0/client/Python/pylibgal3/MANIFEST.in | 2 + 3.0/client/Python/pylibgal3/README | 21 + 3.0/client/Python/pylibgal3/libg3/Errors.py | 51 ++ 3.0/client/Python/pylibgal3/libg3/G3Items.py | 469 ++++++++++++ 3.0/client/Python/pylibgal3/libg3/Gallery3.py | 454 ++++++++++++ 3.0/client/Python/pylibgal3/libg3/Requests.py | 72 ++ 3.0/client/Python/pylibgal3/libg3/__init__.py | 24 + 3.0/client/Python/pylibgal3/setup.py | 36 + 11 files changed, 1820 insertions(+) create mode 100644 3.0/client/Python/pylibgal3/.gitignore create mode 100644 3.0/client/Python/pylibgal3/KNOWN_ISSUES create mode 100644 3.0/client/Python/pylibgal3/LICENSE create mode 100644 3.0/client/Python/pylibgal3/MANIFEST.in create mode 100644 3.0/client/Python/pylibgal3/README create mode 100644 3.0/client/Python/pylibgal3/libg3/Errors.py create mode 100644 3.0/client/Python/pylibgal3/libg3/G3Items.py create mode 100644 3.0/client/Python/pylibgal3/libg3/Gallery3.py create mode 100644 3.0/client/Python/pylibgal3/libg3/Requests.py create mode 100644 3.0/client/Python/pylibgal3/libg3/__init__.py create mode 100644 3.0/client/Python/pylibgal3/setup.py diff --git a/3.0/client/Python/pylibgal3/.gitignore b/3.0/client/Python/pylibgal3/.gitignore new file mode 100644 index 00000000..26cad680 --- /dev/null +++ b/3.0/client/Python/pylibgal3/.gitignore @@ -0,0 +1,4 @@ +*.pyc +test.py +MANIFEST +dist diff --git a/3.0/client/Python/pylibgal3/KNOWN_ISSUES b/3.0/client/Python/pylibgal3/KNOWN_ISSUES new file mode 100644 index 00000000..7cad5280 --- /dev/null +++ b/3.0/client/Python/pylibgal3/KNOWN_ISSUES @@ -0,0 +1,13 @@ +========= +pylibgal3 +========= +* Need to implement member sorting +* Need to implement thumbnail access and manipulation +* Need to implement image resizing +* The getRandomImage() does not currently work + +========= +Gallery 3 +========= +* It appears that the 3.0 release of Gallery has a broken implementation + of the random image retrieval diff --git a/3.0/client/Python/pylibgal3/LICENSE b/3.0/client/Python/pylibgal3/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/3.0/client/Python/pylibgal3/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/3.0/client/Python/pylibgal3/MANIFEST.in b/3.0/client/Python/pylibgal3/MANIFEST.in new file mode 100644 index 00000000..3ca15d46 --- /dev/null +++ b/3.0/client/Python/pylibgal3/MANIFEST.in @@ -0,0 +1,2 @@ +include KNOWN_ISSUES +include LICENSE diff --git a/3.0/client/Python/pylibgal3/README b/3.0/client/Python/pylibgal3/README new file mode 100644 index 00000000..57114ce3 --- /dev/null +++ b/3.0/client/Python/pylibgal3/README @@ -0,0 +1,21 @@ +======= +Install +======= + +To install this libary, just do the usual Python setup: + +# tar -xvzf libgal3-*.tar.gz +# cd libgal3-* +# python setup.py install + +That's it for install. You should be able to import the libg3 library after +that: + +(In your python script) +import libg3 + +============= +Documentation +============= + +See http://stuffivelearned.org/doku.php?id=programming:python:libgal3 for documentation info. diff --git a/3.0/client/Python/pylibgal3/libg3/Errors.py b/3.0/client/Python/pylibgal3/libg3/Errors.py new file mode 100644 index 00000000..399bc259 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/Errors.py @@ -0,0 +1,51 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['G3Error' , 'G3RequestError' , 'G3InvalidRespError' , + 'G3UnknownTypeError' , 'G3AuthError' , 'G3UnknownError'] + +class G3Error(Exception): + pass + +class G3RequestError(G3Error): + def __init__(self , errDict): + self.errors = errDict + self._message = self._getMessage() + + def _getMessage(self): + ret = '' + for e in self.errors.items(): + ret += '%s: %r\n' % e + return ret + + def __str__(self): + return self._message + +class G3InvalidRespError(G3Error): + pass + +class G3UnknownTypeError(G3InvalidRespError): + pass + +class G3AuthError(G3Error): + pass + +class G3UnknownError(G3Error): + pass diff --git a/3.0/client/Python/pylibgal3/libg3/G3Items.py b/3.0/client/Python/pylibgal3/libg3/G3Items.py new file mode 100644 index 00000000..f6477bb8 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/G3Items.py @@ -0,0 +1,469 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['Album' , 'Image' , 'LocalImage' , 'RemoteImage' , 'LocalMovie' , + 'RemoteMovie' , 'getItemFromResp' , 'getItemsFromResp'] + +from datetime import datetime +import json , weakref , types , os , mimetypes , re + +class BaseRemote(object): + def __init__(self , respObj , weakGalObj , weakParent=None): + self._setAttrItems(respObj.items()) + if 'entity' in respObj: + self._setAttrItems(respObj['entity'].items()) + self._weakParent = None + if weakParent is not None: + self._weakParent = weakParent + self._weakGal = weakGalObj + self.fh = None + self._postInit() + + def __getattr__(self , name): + """ + A bit of magic to make the retrieval of member objects lazy + """ + # Process the specials + if name == 'members': + self.members = self._getMemberObjects() + return self.members + if name == 'tags': + self.tags = self._getTags() + return self.tags + if name == 'comments': + self.comments = self._getComments() + return self.comments + # Process the weak reference calls + if name == '_gal': + return self._weakGal() + if name == 'parent' and self._weakParent is not None: + return self._weakParent() + # Process the generic items + urlAttr = '_%s' % name + # Call __getattribute__ to prevent loops + attr = object.__getattribute__(self , urlAttr) + if attr is not None and attr.startswith('http'): + obj = self._getUrlObject(attr) + setattr(self , name , obj) + return obj + raise AttributeError(name) + + def _postInit(self): + """ + This can be overridden in subclasses to do any special initialization + at the end of the __init__ call + """ + pass + + def _setAttrItems(self , d): + for k , v in d: + if k == 'entity': + # Skip it + continue + if (type(v) in types.StringTypes and v.startswith('http') and + 'url' not in k) or k == 'members': + setattr(self , '_%s' % k , v) + else: + setattr(self , k , v) + + def _getMemberObjects(self): + """ + This returns the appropriate objects for each child of this object. + The default "members" attribute only contains the URLs for the + children of this object. This returns a list of the actual objects. + """ + memObjs = self._gal.getItemsForUrls(self._members , self) + return memObjs + + def _getTags(self): + """ + Returns the list of tag objects + + returns(list[Tag]) + """ + # First, I want just the actual tag itself, not the RESTy "tag_item", + # so I'm going to modify the urls to save a step + ret = [] + urls = [] + for url in self.relationships['tags']['members']: + m = re.match('^(.*?/tag)_item(/\d+),\d+$' , url) + urls.append('%s%s' % tuple(m.groups())) + if urls: + for url in urls: + resp = self._gal.getRespFromUrl(url) + ret.append(getItemFromResp(resp , self._gal , self)) + return ret + + def _getComments(self): + """ + Returns a list of the Comment items for this item + + returns(list[Comment]) : Returns a list of Comment objects + """ + ret = [] + # I can't use the shortcut I did for tags so I need to get the list + # of comments in the first call and then create the objects with + # the calls thereafter + commListUrl = self.relationships['comments']['url'] + resp = self._gal.getRespFromUrl(commListUrl) + tmpObj = json.loads(resp.read()) + for url in tmpObj['members']: + resp = self._gal.getRespFromUrl(url) + ret.append(getItemFromResp(resp , self._gal , self)) + return ret + + def _getUrlObject(self , url): + """ + This returns the album cover image + """ + resp = self._gal.getRespFromUrl(url) + return getItemFromResp(resp , self._gal , self) + + def getCrDT(self): + """ + Returns a datetime object for the time this item was created + """ + if hasattr(self , 'created'): + return datetime.fromtimestamp(int(self.created)) + return None + + def getUpdDT(self): + """ + Returns a datetime object for the time this item was last updated + """ + if hasattr(self , 'updated'): + return datetime.fromtimestamp(int(self.updated)) + return None + + def delete(self): + """ + Deletes this + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self._gal.deleteItem(self) + + def update(self , title=None , description=None): + """ + Update either the title, the description or both + + title(str) : The new item title + description(str) : The new item description + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if title is not None: + self.title = title + if description is not None: + self.description = description + return self._gal.updateItem(self) + + def tag(self , tagName): + """ + Tag this item with the string "tagName" + + tagName(str) : The actual tag name + + returns(Tag) : The tag that was created + """ + return self._gal.tagItem(self , tagName) + +class Album(BaseRemote): + def addImage(self , image , title='' , description='' , name=''): + """ + Add a LocalImage object to the album + + image(LocalImage) : The image to upload + + returns(RemoteImage) : The RemoteImage object that was created + """ + if not isinstance(image , LocalImage): + raise TypeError('%r is not of type LocalImage' % image) + return self._gal.addImage(self , image , title , description , name) + + def addMovie(self , movie , name='' , title='' , description=''): + """ + Adds a LocalMovie object to the album + + movie(LocalMovie) : The movie to upload + + returns(RemoteMovie) : The RemoteMovie object that was created + """ + return self._gal.addMovie(self , movie , title , description , name) + + def addAlbum(self , albumName , title , description=''): + """ + Add a subalbum to this album + + albumName(str) : The name of the new album + title(str) : The album title + description(str): The album description + + returns(Album) : The Album object that was created + """ + return self._gal.addAlbum(self , albumName , title , description) + + def setCover(self , image): + """ + Sets the album cover to the RemoteImage + + image(RemoteImage) : The image to set as the album cover + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self._gal.setAlbumCover(self , image) + + def getAlbums(self): + """ + Return a list of the sub-albums in this album + + returns(list[Album]) : A list of Album objects + """ + return self._getByType('album') + Albums = property(getAlbums) + + def getImages(self): + """ + Return a list of the images in this album + + returns(list[RemoteImage]) : A list of RemoteImages + """ + return self._getByType('photo') + Images = property(getImages) + + def getMovies(self): + """ + Return a list of the movies in this album + + returns(list[RemoteMovie]) : A list of RemoteMovie objects + """ + return self._getByType('movie') + Movies = property(getMovies) + + def getRandomImage(self , direct=True): + """ + Returns a random RemoteImage object for the album. If "direct" is + False, a random image can be pulled from nested albums. + + direct(bool) : If set to False, the image may be pulled from + a sub-album + + returns(RemoteImage) : Returns a RemoteImage instance + """ + return self._gal.getRandomImage(self , direct) + + def _getByType(self , t): + ret = [] + for m in self.members: + if m.type == t: + ret.append(m) + return ret + +class Image(object): + contentType = '' + +class LocalImage(Image): + def __init__(self , path , replaceSpaces=True): + if not os.path.isfile(path): + raise IOError('%s is not a file' % path) + self.path = path + self.replaceSpaces = replaceSpaces + self.Filename = os.path.basename(self.path) + self.fh = None + self.type = 'photo' + + def setContentType(self , ctype=None): + if ctype is not None: + self.contentType = ctype + self.contentType = mimetypes.guess_type(self.getFileContents())[0] or \ + 'application/octet-stream' + def getContentType(self): + if not self.contentType: + self.setContentType() + return self.contentType + ContentType = property(getContentType , setContentType) + + def setFilename(self , name): + self.filename = name + if self.replaceSpaces: + self.filename = self.filename.replace(' ' , '_') + def getFilename(self): + return self.filename + Filename = property(getFilename , setFilename) + + def getFileContents(self): + """ + Gets the entire contents of the file + + returns(str) : File contents + """ + if self.fh is None: + self.fh = open(self.path , 'rb') + self.fh.seek(0) + return self.fh.read() + + def getUploadContent(self): + """ + This will return a string containing the MIME headers and the actual + binary content to be uploaded + """ + ret = 'Content-Disposition: form-data; name="file"; ' + ret += 'filename="%s"\r\n' % self.filename + ret += 'Content-Type: %s\r\n' % self.ContentType + ret += 'Content-Transfer-Encoding: binary\r\n' + ret += '\r\n' + ret += self.getFileContents() + '\r\n' + return ret + + def close(self): + try: + self.fh.close() + except: + pass + +class RemoteImage(BaseRemote , Image): + def addComment(self , comment): + """ + Comment on this item with the string "comment" + + comment(str) : The comment + + returns(Comment) : The comment that was created + """ + return self._gal.addComment(self , comment) + + def read(self , length=None): + if not self.fh: + resp = self._gal.getRespFromUrl(self.file_url) + self.fh = resp + if length is None: + return self.fh.read() + return self.fh.read(int(length)) + + def close(self): + try: + self.fh.close() + except: + pass + +class LocalMovie(LocalImage): + def __init__(self , path , replaceSpaces=True): + LocalImage.__init__(self , path , replaceSpaces) + self.type = 'movie' + +class RemoteMovie(RemoteImage): + pass + +class Tag(BaseRemote): + """ + A simple class to represent a tag + """ + def __str__(self): + return self.name + + def _postInit(self): + if hasattr(self , 'count'): + self.count = int(self.count) + self.type = 'tag' + + def tag(self , tagName): + raise G3Error('You cannot tag a tag') + +class Comment(BaseRemote): + """ + A class to represent a comment + """ + def __str__(self): + return self.text + + def _postInit(self): + # Change the "item" attribute to "parent" since that's what it is + # I'm doing this to address overall consistency + self._parent = None + if hasattr(self , '_item'): + self._parent = getattr(self , '_item') + + def tag(self , tagName): + raise G3Error('You cannot tag a comment') + +def getItemFromResp(response , galObj , parent=None): + """ + Returns the appropriate item given the "addinfourl" response object from + the urllib2 request + + response(addinfourl|dict) : The response object from the urllib2 request + or a dict that has already been converted + (usually when called from getItemsFromResp) + galObj(Gallery3) : The gallery object this is associated with + parent(Album) : The parent object for this item + + returns(BaseRemote) : Returns an implemenation of BaseRemote + """ + galObj = weakref.ref(galObj) + if parent is not None: + parent = weakref.ref(parent) + if isinstance(response , dict): + respObj = response + else: + respObj = json.loads(response.read()) + if 'count' in respObj['entity']: + # This is a tag. It doesn't have the same items as regular objects + return Tag(respObj , galObj , parent) + if 'text' in respObj['entity']: + # This is a comment. It also does not have the same items as + # regular objects + return Comment(respObj , galObj , parent) + try: + t = respObj['entity']['type'] + except: + raise G3InvalidRespError('Response contains no "entity type": %r' % + response) + if t == 'album': + return Album(respObj , galObj , parent) + elif t == 'photo': + return RemoteImage(respObj , galObj , parent) + elif t == 'movie': + return RemoteMovie(respObj , galObj , parent) + else: + raise G3UnknownTypeError('Unknown entity type: %s' % t) + +def getItemsFromResp(response , galObj , parent=None): + """ + This takes the raw response with a list of items and returns a list of + the corresponding objects + + response(addinfourl|dict) : The response object from the urllib2 request + or a dict that has already been converted + (usually when called from getItemsFromResp) + galObj(Gallery3) : The gallery object this is associated with + parent(Album) : The parent object for this item + + returns(list[BaseRemote]) : Returns a list of BaseRemote objects + """ + ret = [] + lResp = json.loads(response.read()) + if not isinstance(lResp , list): + lResp = list(lResp) + for resp in lResp: + ret.append(getItemFromResp(resp , galObj , parent)) + return ret diff --git a/3.0/client/Python/pylibgal3/libg3/Gallery3.py b/3.0/client/Python/pylibgal3/libg3/Gallery3.py new file mode 100644 index 00000000..924d7d86 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/Gallery3.py @@ -0,0 +1,454 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['Gallery3' , 'login'] + +from Requests import * +from Errors import G3RequestError , G3UnknownError +from G3Items import getItemFromResp , getItemsFromResp , BaseRemote , Album , \ + RemoteImage , Tag +from urllib import quote , urlencode +from uuid import uuid4 +import urllib2 , os , json + +class Gallery3(object): + """ + This is the main utility class that should be instantiated and used for all + calls + """ + def __init__(self , host , apiKey , g3Base='/gallery3' , port=80 , + ssl=False): + """ + Initializes and sets up the gallery 3 object + + host(str) : The hostname of the gallery site + apiKey(str) : The api key to use for the connections + g3Base(str) : The remote url path to your gallery 3 install + (default: /gallery3) + port(int) : The port number to connect to (default: 80) + ssl(bool) : If true, use SSL for the connection (default: 80) + """ + self.host = host + self.apiKey = apiKey + self.port = int(port) + self.ssl = ssl + self.g3Base = g3Base.strip('/') + self.protocol = ('http' , 'https')[ssl] + self.root = None + self._rootUri = 'index.php/rest/item/1' + self._opener = None + self._buildOpener() + + def getRoot(self): + """ + Returns the root item (album) + """ + if self.root is None: + resp = self.getRespFromUri(self._rootUri) + self.root = getItemFromResp(resp , self) + return self.root + + def getRandomImage(self , album , direct=True): + """ + Returns a random RemoteImage object for the album. If "direct" is + False, a random image can be pulled from nested albums. + + album(Album) : The album object to pull the random image from + direct(bool) : If set to False, the image may be pulled from + a sub-album + + returns(RemoteImage) : Returns a RemoteImage instance + """ + scope = ('all' , 'direct')[direct] + data = { + 'type': 'photo' , + 'random': 'true' , + 'scope': scope , + } + url = '%s?%s' % (album.url , urlencode(data)) + resp = self.getRespFromUrl(url) + return getItemFromResp(resp , self) + + def getItemsForUrls(self , urls , parent=None): + """ + This retrieves an item for each url specified in the urls list + + urls(list[str]) : The list of urls to retrieve + + returns(list[BaseRemote]) : Returns a list of the corresponding + remote objects + """ + data = { + 'urls': json.dumps(urls) , + } + resp = self.getRespFromUri('index.php/rest/items' , data) + return getItemsFromResp(resp , self , parent) + + def getRespFromUrl(self , url): + """ + This returns the response object given a full url rather than just a + uri defining the location on the server + + url(str) : The url to the resource + """ + req = GetRequest(url , self.apiKey) + resp = self._openReq(req) + return resp + + def getRespFromUri(self , uri , kwargs={}): + """ + Performs the request for the given uri and returns the "addinfourl" + response + + uri(str) : The uri string defining the resource on the defined host + """ + url = self._buildUrl(uri , kwargs) + return self.getRespFromUrl(url) + + def addAlbum(self , parent , albumName , title , description=''): + """ + Adds an album to the given parent album + + parent(Album) : The parent Album object + albumName(str) : The name of the album + title(str) : The album title + description(str) : The album description + + returns(Album) : The Album object that was created + """ + if not parent.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + parent.title) + data = { + 'type': 'album' , + 'name': albumName , + 'title': title , + 'description': description , + } + req = PostRequest(parent.url , self.apiKey , data) + resp = self._openReq(req) + newObjUrl = self._getUrlFromResp(resp) + item = getItemFromResp(self.getRespFromUrl(newObjUrl) , self , parent) + parent._members.append(newObjUrl) + parent.members.append(item) + return item + + def addImage(self , parent , image , title='' , description='' , name=''): + """ + Add a LocalImage to the parent album. + + parent(Album) : The parent album to add the image to + image(LocalImage) : The local image to upload and add to the + parent + title(str) : The image title + description(str) : The image description + name(str) : The image file name + + returns(RemoteImage) : The RemoteImage instance for the item + uploaded + """ + if not parent.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + parent.title) + if name: + image.Filename = name + entity = { + 'name': image.filename , + 'type': image.type , + 'title': title , + 'description': description , + } + boundary = str(uuid4()) + headers = {'Content-Type': 'multipart/form-data; boundary=%s' % + boundary} + # this is more complicated than adding an album. We have to + # construct the upload MIME headers, including build the string + # data section + data = '--%s\r\n' % boundary + data += 'Content-Disposition: form-data; name="entity"\r\n' + data += 'Content-Type: text/plain; ' \ + 'charset=UTF-8\r\n' + data += 'Content-Transfer-Encoding: 8bit\r\n' + data += '\r\n' + data += '%s\r\n' % json.dumps(entity , separators=(',' , ':')) + data += '--%s\r\n' % boundary + data += image.getUploadContent() + data += '--%s--\r\n' % boundary + req = PostRequest(parent.url , self.apiKey , data , headers) + resp = self._openReq(req) + newObjUrl = self._getUrlFromResp(resp) + item = getItemFromResp(self.getRespFromUrl(newObjUrl) , self , parent) + parent._members.append(newObjUrl) + parent.members.append(item) + return item + + def addMovie(self , parent , movie , title='' , description='' , name=''): + """ + Add a LocalMovie to the parent album. + + parent(Album) : The parent album to add the movie to + image(LocalMovie) : The local movie to upload and add to the + parent + title(str) : The movie title + description(str) : The movie description + name(str) : The movie file name + + returns(RemoteMovie) : The RemoteMovie instance for the movie + uploaded + """ + return self.addImage(parent , movie , title , description , name) + + def setAlbumCover(self , album , image): + """ + Updates a remote item's title and description + + album(Album) : The album to set the cover on + image(RemoteImage) : The image to use as the cover + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if not album.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + album.title) + try: + self._isItemValid(album , Album) + self._isItemValid(image , RemoteImage) + except Exception , e: + return (False , str(e)) + data = { + 'album_cover': image.url , + } + req = PutRequest(album.url , self.apiKey , data) + try: + resp = self._openReq(req) + except G3RequestError , e: + return (False , str(e)) + album.album_cover = image + album._album_cover = image.url + return (True , '') + + def updateItem(self , item): + """ + Updates a remote item's title and description + + item(BaseRemote) : An item descended from BaseRemote + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if not item.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + item.title) + try: + self._isItemValid(item , BaseRemote) + except Exception , e: + return (False , str(e)) + data = { + 'title': item.title , + 'description': item.description , + } + req = PutRequest(item.url , self.apiKey , data) + try: + resp = self._openReq(req) + except G3RequestError , e: + return (False , str(e)) + return (True , '') + + def updateAlbum(self , album): + """ + Update the title and description for an album. + + image(Album) : Updates the title and/or description + for the Album + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self.updateItem(album) + + def updateImage(self , image): + """ + Update the title and description for an image. + + image(RemoteImage) : Updates the title and/or description for the + RemoteImage + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self.updateItem(image) + + def updateMovie(self , movie): + """ + Update the title and description for a movie. + + image(RemoteMovie) : Updates the title and/or description for the + RemoteMovie + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self.updateItem(movie) + + def deleteItem(self , item): + """ + Deletes the given item. Item must be descended from BaseRemote. + + item(BaseRemote) : The item to delete + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if not item.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + item.title) + try: + self._isItemValid(item , BaseRemote) + except Exception , e: + return (False , e.message) + req = DeleteRequest(item.url , self.apiKey) + try: + resp = self._openReq(req) + except G3RequestError , e: + return (False , e.message) + return (True , '') + + def tagItem(self , item , tagName): + """ + Tag this item with the string "tagName" + + tagName(str) : The actual tag name + + returns(Tag) : The tag that was created + """ + # First we have to create the tag itself, if necessary + data = { + 'name': str(tagName) , + } + url = self._buildUrl('index.php/rest/tags') + req = PostRequest(url , self.apiKey , data) + resp = self._openReq(req) + r = json.loads(resp.read()) + tagUrl = r['url'] + # And now that we have our (possibly) newly created tag, we can + # use that to tag our item + data = { + 'tag': tagUrl , + 'item': item.url , + } + url = self._buildUrl('index.php/rest/item_tags/%s' % item.id) + req = PostRequest(url , self.apiKey , data) + resp = self._openReq(req) + respObj = json.loads(resp.read()) + item.relationships['tags']['members'].append(respObj['url']) + tag = Tag(respObj , self , item) + if hasattr(item , 'tags'): + item.tags.append(tag) + return tag + + def addComment(self , image , comment): + """ + Comment on this item with the string "comment" + + comment(str) : The comment + + returns(Comment) : The comment that was created + """ + data = { + 'item': image.url , + 'text': comment , + } + url = self._buildUrl('index.php/rest/comments') + req = PostRequest(url , self.apiKey , data) + resp = self._openReq(req) + commUrl = json.loads(resp.read())['url'] + resp = self.getRespFromUrl(commUrl) + comm = getItemFromResp(resp , self , image) + if hasattr(image , 'comments'): + image.comments.append(comm) + return comm + + def _buildOpener(self): + cp = urllib2.HTTPCookieProcessor() + self._opener = urllib2.build_opener(cp) + if self.ssl: + self._opener.add_handler(urllib2.HTTPSHandler()) + + def _buildUrl(self , resource , kwargs={}): + url = '%s://%s:%d/%s/%s' % (self.protocol , self.host , self.port , + quote(self.g3Base) , quote(resource)) + if kwargs: + url += '?%s' % urlencode(kwargs) + return url + + def _getUrlFromResp(self , resp): + d = json.loads(resp.read()) + return d['url'] + + def _openReq(self , req): + try: + resp = self._opener.open(req) + except urllib2.HTTPError , e: + err = json.loads(e.read()) + if isinstance(err , dict) and 'errors' in err: + raise G3RequestError(err['errors']) + else: + raise G3UnknownError('Unknown request error: %s' % e) + return resp + + def _isItemValid(self , item , cls): + if not isinstance(item , cls): + raise TypeError('Items to be modified must be descended from ' + '%s: %s' % (cls , type(item))) + if not 'url' in item.__dict__: + raise G3UnknownError('The object, %s, has no "url"' % item) + +def login(host , username , passwd , g3Base='/gallery3' , port=80 , + ssl=False): + """ + This will log you in and return a Gallery3 object on success, None + otherwise + + host(str) : The hostname of the gallery site + username(str) : The username to login with + passwd(str) : The password to login with + g3Base(str) : The remote url path to your gallery 3 install + (default: /gallery3) + port(int) : The port number to connect to (default: 80) + ssl(bool) : If true, use SSL for the connection (default: 80) + """ + data = { + 'user': username , + 'password': passwd , + } + protocol = ('http' , 'https')[ssl] + url = '%s://%s:%d/%s/index.php/rest' % (protocol , host , port , + quote(g3Base)) + req = PostRequest(url , None , urlencode(data)) + opener = urllib2.build_opener() + if ssl: + opener.add_handler(urllib2.HTTPSHandler()) + try: + resp = opener.open(req) + except urllib2.HTTPError , e: + return None + apiKey = resp.read().strip('\'"') + return Gallery3(host , apiKey , g3Base , port , ssl) diff --git a/3.0/client/Python/pylibgal3/libg3/Requests.py b/3.0/client/Python/pylibgal3/libg3/Requests.py new file mode 100644 index 00000000..7b0fedb6 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/Requests.py @@ -0,0 +1,72 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['BaseRequest' , 'GetRequest' , 'PostRequest' , 'PutRequest' , + 'DeleteRequest'] + +from urllib2 import Request +from urllib import quote +import os , json , types + +class BaseRequest(Request): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + if apiKey is not None: + headers['X-Gallery-Request-Key'] = apiKey + if data is not None: + if isinstance(data , dict): + data = 'entity=%s' % quote(json.dumps(data , + separators=(',' , ':'))) + elif type(data) not in types.StringTypes: + raise TypeError('Invalid type for data. It should be ' + 'a "dict" or "str", not %s' % type(data)) + headers['Content-Length'] = str(len(data)) + Request.__init__(self , url , data , headers , origin_req_host , + unverifiable) + +class GetRequest(BaseRequest): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'get' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) + +class PostRequest(BaseRequest): + def __init__(self , url , apiKey , data , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'post' + if 'Content-Type' not in headers: + headers['Content-Type'] = 'application/x-www-form-urlencoded' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) + +class PutRequest(BaseRequest): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'put' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) + +class DeleteRequest(BaseRequest): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'delete' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) diff --git a/3.0/client/Python/pylibgal3/libg3/__init__.py b/3.0/client/Python/pylibgal3/libg3/__init__.py new file mode 100644 index 00000000..745ba345 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/__init__.py @@ -0,0 +1,24 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +from G3Items import * +from Gallery3 import * + +__version__ = '0.1.3' diff --git a/3.0/client/Python/pylibgal3/setup.py b/3.0/client/Python/pylibgal3/setup.py new file mode 100644 index 00000000..ba9d1d82 --- /dev/null +++ b/3.0/client/Python/pylibgal3/setup.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + + +from distutils.core import setup +from libg3 import __version__ as version +import sys + +setup(name='pylibgal3' , + version=version , + author='Jay Deiman' , + author_email='admin@splitstreams.com' , + url='http://stuffivelearned.org' , + description='A library for accessing/manipulating a Gallery 3 install' , + packages=['libg3'] , + package_dir={'libg3': 'libg3'} , +) From 6dfa636c3e72b53b9d952864c0768244a486cbb3 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Wed, 22 Dec 2010 01:45:11 +0800 Subject: [PATCH 161/300] Adding the pylibgal3 source --- 3.0/client/Python/pylibgal3/.gitignore | 4 + 3.0/client/Python/pylibgal3/KNOWN_ISSUES | 13 + 3.0/client/Python/pylibgal3/LICENSE | 674 ++++++++++++++++++ 3.0/client/Python/pylibgal3/MANIFEST.in | 2 + 3.0/client/Python/pylibgal3/README | 21 + 3.0/client/Python/pylibgal3/libg3/Errors.py | 51 ++ 3.0/client/Python/pylibgal3/libg3/G3Items.py | 469 ++++++++++++ 3.0/client/Python/pylibgal3/libg3/Gallery3.py | 454 ++++++++++++ 3.0/client/Python/pylibgal3/libg3/Requests.py | 72 ++ 3.0/client/Python/pylibgal3/libg3/__init__.py | 24 + 3.0/client/Python/pylibgal3/setup.py | 36 + 11 files changed, 1820 insertions(+) create mode 100644 3.0/client/Python/pylibgal3/.gitignore create mode 100644 3.0/client/Python/pylibgal3/KNOWN_ISSUES create mode 100644 3.0/client/Python/pylibgal3/LICENSE create mode 100644 3.0/client/Python/pylibgal3/MANIFEST.in create mode 100644 3.0/client/Python/pylibgal3/README create mode 100644 3.0/client/Python/pylibgal3/libg3/Errors.py create mode 100644 3.0/client/Python/pylibgal3/libg3/G3Items.py create mode 100644 3.0/client/Python/pylibgal3/libg3/Gallery3.py create mode 100644 3.0/client/Python/pylibgal3/libg3/Requests.py create mode 100644 3.0/client/Python/pylibgal3/libg3/__init__.py create mode 100644 3.0/client/Python/pylibgal3/setup.py diff --git a/3.0/client/Python/pylibgal3/.gitignore b/3.0/client/Python/pylibgal3/.gitignore new file mode 100644 index 00000000..26cad680 --- /dev/null +++ b/3.0/client/Python/pylibgal3/.gitignore @@ -0,0 +1,4 @@ +*.pyc +test.py +MANIFEST +dist diff --git a/3.0/client/Python/pylibgal3/KNOWN_ISSUES b/3.0/client/Python/pylibgal3/KNOWN_ISSUES new file mode 100644 index 00000000..7cad5280 --- /dev/null +++ b/3.0/client/Python/pylibgal3/KNOWN_ISSUES @@ -0,0 +1,13 @@ +========= +pylibgal3 +========= +* Need to implement member sorting +* Need to implement thumbnail access and manipulation +* Need to implement image resizing +* The getRandomImage() does not currently work + +========= +Gallery 3 +========= +* It appears that the 3.0 release of Gallery has a broken implementation + of the random image retrieval diff --git a/3.0/client/Python/pylibgal3/LICENSE b/3.0/client/Python/pylibgal3/LICENSE new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/3.0/client/Python/pylibgal3/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/3.0/client/Python/pylibgal3/MANIFEST.in b/3.0/client/Python/pylibgal3/MANIFEST.in new file mode 100644 index 00000000..3ca15d46 --- /dev/null +++ b/3.0/client/Python/pylibgal3/MANIFEST.in @@ -0,0 +1,2 @@ +include KNOWN_ISSUES +include LICENSE diff --git a/3.0/client/Python/pylibgal3/README b/3.0/client/Python/pylibgal3/README new file mode 100644 index 00000000..57114ce3 --- /dev/null +++ b/3.0/client/Python/pylibgal3/README @@ -0,0 +1,21 @@ +======= +Install +======= + +To install this libary, just do the usual Python setup: + +# tar -xvzf libgal3-*.tar.gz +# cd libgal3-* +# python setup.py install + +That's it for install. You should be able to import the libg3 library after +that: + +(In your python script) +import libg3 + +============= +Documentation +============= + +See http://stuffivelearned.org/doku.php?id=programming:python:libgal3 for documentation info. diff --git a/3.0/client/Python/pylibgal3/libg3/Errors.py b/3.0/client/Python/pylibgal3/libg3/Errors.py new file mode 100644 index 00000000..399bc259 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/Errors.py @@ -0,0 +1,51 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['G3Error' , 'G3RequestError' , 'G3InvalidRespError' , + 'G3UnknownTypeError' , 'G3AuthError' , 'G3UnknownError'] + +class G3Error(Exception): + pass + +class G3RequestError(G3Error): + def __init__(self , errDict): + self.errors = errDict + self._message = self._getMessage() + + def _getMessage(self): + ret = '' + for e in self.errors.items(): + ret += '%s: %r\n' % e + return ret + + def __str__(self): + return self._message + +class G3InvalidRespError(G3Error): + pass + +class G3UnknownTypeError(G3InvalidRespError): + pass + +class G3AuthError(G3Error): + pass + +class G3UnknownError(G3Error): + pass diff --git a/3.0/client/Python/pylibgal3/libg3/G3Items.py b/3.0/client/Python/pylibgal3/libg3/G3Items.py new file mode 100644 index 00000000..f6477bb8 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/G3Items.py @@ -0,0 +1,469 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['Album' , 'Image' , 'LocalImage' , 'RemoteImage' , 'LocalMovie' , + 'RemoteMovie' , 'getItemFromResp' , 'getItemsFromResp'] + +from datetime import datetime +import json , weakref , types , os , mimetypes , re + +class BaseRemote(object): + def __init__(self , respObj , weakGalObj , weakParent=None): + self._setAttrItems(respObj.items()) + if 'entity' in respObj: + self._setAttrItems(respObj['entity'].items()) + self._weakParent = None + if weakParent is not None: + self._weakParent = weakParent + self._weakGal = weakGalObj + self.fh = None + self._postInit() + + def __getattr__(self , name): + """ + A bit of magic to make the retrieval of member objects lazy + """ + # Process the specials + if name == 'members': + self.members = self._getMemberObjects() + return self.members + if name == 'tags': + self.tags = self._getTags() + return self.tags + if name == 'comments': + self.comments = self._getComments() + return self.comments + # Process the weak reference calls + if name == '_gal': + return self._weakGal() + if name == 'parent' and self._weakParent is not None: + return self._weakParent() + # Process the generic items + urlAttr = '_%s' % name + # Call __getattribute__ to prevent loops + attr = object.__getattribute__(self , urlAttr) + if attr is not None and attr.startswith('http'): + obj = self._getUrlObject(attr) + setattr(self , name , obj) + return obj + raise AttributeError(name) + + def _postInit(self): + """ + This can be overridden in subclasses to do any special initialization + at the end of the __init__ call + """ + pass + + def _setAttrItems(self , d): + for k , v in d: + if k == 'entity': + # Skip it + continue + if (type(v) in types.StringTypes and v.startswith('http') and + 'url' not in k) or k == 'members': + setattr(self , '_%s' % k , v) + else: + setattr(self , k , v) + + def _getMemberObjects(self): + """ + This returns the appropriate objects for each child of this object. + The default "members" attribute only contains the URLs for the + children of this object. This returns a list of the actual objects. + """ + memObjs = self._gal.getItemsForUrls(self._members , self) + return memObjs + + def _getTags(self): + """ + Returns the list of tag objects + + returns(list[Tag]) + """ + # First, I want just the actual tag itself, not the RESTy "tag_item", + # so I'm going to modify the urls to save a step + ret = [] + urls = [] + for url in self.relationships['tags']['members']: + m = re.match('^(.*?/tag)_item(/\d+),\d+$' , url) + urls.append('%s%s' % tuple(m.groups())) + if urls: + for url in urls: + resp = self._gal.getRespFromUrl(url) + ret.append(getItemFromResp(resp , self._gal , self)) + return ret + + def _getComments(self): + """ + Returns a list of the Comment items for this item + + returns(list[Comment]) : Returns a list of Comment objects + """ + ret = [] + # I can't use the shortcut I did for tags so I need to get the list + # of comments in the first call and then create the objects with + # the calls thereafter + commListUrl = self.relationships['comments']['url'] + resp = self._gal.getRespFromUrl(commListUrl) + tmpObj = json.loads(resp.read()) + for url in tmpObj['members']: + resp = self._gal.getRespFromUrl(url) + ret.append(getItemFromResp(resp , self._gal , self)) + return ret + + def _getUrlObject(self , url): + """ + This returns the album cover image + """ + resp = self._gal.getRespFromUrl(url) + return getItemFromResp(resp , self._gal , self) + + def getCrDT(self): + """ + Returns a datetime object for the time this item was created + """ + if hasattr(self , 'created'): + return datetime.fromtimestamp(int(self.created)) + return None + + def getUpdDT(self): + """ + Returns a datetime object for the time this item was last updated + """ + if hasattr(self , 'updated'): + return datetime.fromtimestamp(int(self.updated)) + return None + + def delete(self): + """ + Deletes this + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self._gal.deleteItem(self) + + def update(self , title=None , description=None): + """ + Update either the title, the description or both + + title(str) : The new item title + description(str) : The new item description + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if title is not None: + self.title = title + if description is not None: + self.description = description + return self._gal.updateItem(self) + + def tag(self , tagName): + """ + Tag this item with the string "tagName" + + tagName(str) : The actual tag name + + returns(Tag) : The tag that was created + """ + return self._gal.tagItem(self , tagName) + +class Album(BaseRemote): + def addImage(self , image , title='' , description='' , name=''): + """ + Add a LocalImage object to the album + + image(LocalImage) : The image to upload + + returns(RemoteImage) : The RemoteImage object that was created + """ + if not isinstance(image , LocalImage): + raise TypeError('%r is not of type LocalImage' % image) + return self._gal.addImage(self , image , title , description , name) + + def addMovie(self , movie , name='' , title='' , description=''): + """ + Adds a LocalMovie object to the album + + movie(LocalMovie) : The movie to upload + + returns(RemoteMovie) : The RemoteMovie object that was created + """ + return self._gal.addMovie(self , movie , title , description , name) + + def addAlbum(self , albumName , title , description=''): + """ + Add a subalbum to this album + + albumName(str) : The name of the new album + title(str) : The album title + description(str): The album description + + returns(Album) : The Album object that was created + """ + return self._gal.addAlbum(self , albumName , title , description) + + def setCover(self , image): + """ + Sets the album cover to the RemoteImage + + image(RemoteImage) : The image to set as the album cover + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self._gal.setAlbumCover(self , image) + + def getAlbums(self): + """ + Return a list of the sub-albums in this album + + returns(list[Album]) : A list of Album objects + """ + return self._getByType('album') + Albums = property(getAlbums) + + def getImages(self): + """ + Return a list of the images in this album + + returns(list[RemoteImage]) : A list of RemoteImages + """ + return self._getByType('photo') + Images = property(getImages) + + def getMovies(self): + """ + Return a list of the movies in this album + + returns(list[RemoteMovie]) : A list of RemoteMovie objects + """ + return self._getByType('movie') + Movies = property(getMovies) + + def getRandomImage(self , direct=True): + """ + Returns a random RemoteImage object for the album. If "direct" is + False, a random image can be pulled from nested albums. + + direct(bool) : If set to False, the image may be pulled from + a sub-album + + returns(RemoteImage) : Returns a RemoteImage instance + """ + return self._gal.getRandomImage(self , direct) + + def _getByType(self , t): + ret = [] + for m in self.members: + if m.type == t: + ret.append(m) + return ret + +class Image(object): + contentType = '' + +class LocalImage(Image): + def __init__(self , path , replaceSpaces=True): + if not os.path.isfile(path): + raise IOError('%s is not a file' % path) + self.path = path + self.replaceSpaces = replaceSpaces + self.Filename = os.path.basename(self.path) + self.fh = None + self.type = 'photo' + + def setContentType(self , ctype=None): + if ctype is not None: + self.contentType = ctype + self.contentType = mimetypes.guess_type(self.getFileContents())[0] or \ + 'application/octet-stream' + def getContentType(self): + if not self.contentType: + self.setContentType() + return self.contentType + ContentType = property(getContentType , setContentType) + + def setFilename(self , name): + self.filename = name + if self.replaceSpaces: + self.filename = self.filename.replace(' ' , '_') + def getFilename(self): + return self.filename + Filename = property(getFilename , setFilename) + + def getFileContents(self): + """ + Gets the entire contents of the file + + returns(str) : File contents + """ + if self.fh is None: + self.fh = open(self.path , 'rb') + self.fh.seek(0) + return self.fh.read() + + def getUploadContent(self): + """ + This will return a string containing the MIME headers and the actual + binary content to be uploaded + """ + ret = 'Content-Disposition: form-data; name="file"; ' + ret += 'filename="%s"\r\n' % self.filename + ret += 'Content-Type: %s\r\n' % self.ContentType + ret += 'Content-Transfer-Encoding: binary\r\n' + ret += '\r\n' + ret += self.getFileContents() + '\r\n' + return ret + + def close(self): + try: + self.fh.close() + except: + pass + +class RemoteImage(BaseRemote , Image): + def addComment(self , comment): + """ + Comment on this item with the string "comment" + + comment(str) : The comment + + returns(Comment) : The comment that was created + """ + return self._gal.addComment(self , comment) + + def read(self , length=None): + if not self.fh: + resp = self._gal.getRespFromUrl(self.file_url) + self.fh = resp + if length is None: + return self.fh.read() + return self.fh.read(int(length)) + + def close(self): + try: + self.fh.close() + except: + pass + +class LocalMovie(LocalImage): + def __init__(self , path , replaceSpaces=True): + LocalImage.__init__(self , path , replaceSpaces) + self.type = 'movie' + +class RemoteMovie(RemoteImage): + pass + +class Tag(BaseRemote): + """ + A simple class to represent a tag + """ + def __str__(self): + return self.name + + def _postInit(self): + if hasattr(self , 'count'): + self.count = int(self.count) + self.type = 'tag' + + def tag(self , tagName): + raise G3Error('You cannot tag a tag') + +class Comment(BaseRemote): + """ + A class to represent a comment + """ + def __str__(self): + return self.text + + def _postInit(self): + # Change the "item" attribute to "parent" since that's what it is + # I'm doing this to address overall consistency + self._parent = None + if hasattr(self , '_item'): + self._parent = getattr(self , '_item') + + def tag(self , tagName): + raise G3Error('You cannot tag a comment') + +def getItemFromResp(response , galObj , parent=None): + """ + Returns the appropriate item given the "addinfourl" response object from + the urllib2 request + + response(addinfourl|dict) : The response object from the urllib2 request + or a dict that has already been converted + (usually when called from getItemsFromResp) + galObj(Gallery3) : The gallery object this is associated with + parent(Album) : The parent object for this item + + returns(BaseRemote) : Returns an implemenation of BaseRemote + """ + galObj = weakref.ref(galObj) + if parent is not None: + parent = weakref.ref(parent) + if isinstance(response , dict): + respObj = response + else: + respObj = json.loads(response.read()) + if 'count' in respObj['entity']: + # This is a tag. It doesn't have the same items as regular objects + return Tag(respObj , galObj , parent) + if 'text' in respObj['entity']: + # This is a comment. It also does not have the same items as + # regular objects + return Comment(respObj , galObj , parent) + try: + t = respObj['entity']['type'] + except: + raise G3InvalidRespError('Response contains no "entity type": %r' % + response) + if t == 'album': + return Album(respObj , galObj , parent) + elif t == 'photo': + return RemoteImage(respObj , galObj , parent) + elif t == 'movie': + return RemoteMovie(respObj , galObj , parent) + else: + raise G3UnknownTypeError('Unknown entity type: %s' % t) + +def getItemsFromResp(response , galObj , parent=None): + """ + This takes the raw response with a list of items and returns a list of + the corresponding objects + + response(addinfourl|dict) : The response object from the urllib2 request + or a dict that has already been converted + (usually when called from getItemsFromResp) + galObj(Gallery3) : The gallery object this is associated with + parent(Album) : The parent object for this item + + returns(list[BaseRemote]) : Returns a list of BaseRemote objects + """ + ret = [] + lResp = json.loads(response.read()) + if not isinstance(lResp , list): + lResp = list(lResp) + for resp in lResp: + ret.append(getItemFromResp(resp , galObj , parent)) + return ret diff --git a/3.0/client/Python/pylibgal3/libg3/Gallery3.py b/3.0/client/Python/pylibgal3/libg3/Gallery3.py new file mode 100644 index 00000000..924d7d86 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/Gallery3.py @@ -0,0 +1,454 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['Gallery3' , 'login'] + +from Requests import * +from Errors import G3RequestError , G3UnknownError +from G3Items import getItemFromResp , getItemsFromResp , BaseRemote , Album , \ + RemoteImage , Tag +from urllib import quote , urlencode +from uuid import uuid4 +import urllib2 , os , json + +class Gallery3(object): + """ + This is the main utility class that should be instantiated and used for all + calls + """ + def __init__(self , host , apiKey , g3Base='/gallery3' , port=80 , + ssl=False): + """ + Initializes and sets up the gallery 3 object + + host(str) : The hostname of the gallery site + apiKey(str) : The api key to use for the connections + g3Base(str) : The remote url path to your gallery 3 install + (default: /gallery3) + port(int) : The port number to connect to (default: 80) + ssl(bool) : If true, use SSL for the connection (default: 80) + """ + self.host = host + self.apiKey = apiKey + self.port = int(port) + self.ssl = ssl + self.g3Base = g3Base.strip('/') + self.protocol = ('http' , 'https')[ssl] + self.root = None + self._rootUri = 'index.php/rest/item/1' + self._opener = None + self._buildOpener() + + def getRoot(self): + """ + Returns the root item (album) + """ + if self.root is None: + resp = self.getRespFromUri(self._rootUri) + self.root = getItemFromResp(resp , self) + return self.root + + def getRandomImage(self , album , direct=True): + """ + Returns a random RemoteImage object for the album. If "direct" is + False, a random image can be pulled from nested albums. + + album(Album) : The album object to pull the random image from + direct(bool) : If set to False, the image may be pulled from + a sub-album + + returns(RemoteImage) : Returns a RemoteImage instance + """ + scope = ('all' , 'direct')[direct] + data = { + 'type': 'photo' , + 'random': 'true' , + 'scope': scope , + } + url = '%s?%s' % (album.url , urlencode(data)) + resp = self.getRespFromUrl(url) + return getItemFromResp(resp , self) + + def getItemsForUrls(self , urls , parent=None): + """ + This retrieves an item for each url specified in the urls list + + urls(list[str]) : The list of urls to retrieve + + returns(list[BaseRemote]) : Returns a list of the corresponding + remote objects + """ + data = { + 'urls': json.dumps(urls) , + } + resp = self.getRespFromUri('index.php/rest/items' , data) + return getItemsFromResp(resp , self , parent) + + def getRespFromUrl(self , url): + """ + This returns the response object given a full url rather than just a + uri defining the location on the server + + url(str) : The url to the resource + """ + req = GetRequest(url , self.apiKey) + resp = self._openReq(req) + return resp + + def getRespFromUri(self , uri , kwargs={}): + """ + Performs the request for the given uri and returns the "addinfourl" + response + + uri(str) : The uri string defining the resource on the defined host + """ + url = self._buildUrl(uri , kwargs) + return self.getRespFromUrl(url) + + def addAlbum(self , parent , albumName , title , description=''): + """ + Adds an album to the given parent album + + parent(Album) : The parent Album object + albumName(str) : The name of the album + title(str) : The album title + description(str) : The album description + + returns(Album) : The Album object that was created + """ + if not parent.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + parent.title) + data = { + 'type': 'album' , + 'name': albumName , + 'title': title , + 'description': description , + } + req = PostRequest(parent.url , self.apiKey , data) + resp = self._openReq(req) + newObjUrl = self._getUrlFromResp(resp) + item = getItemFromResp(self.getRespFromUrl(newObjUrl) , self , parent) + parent._members.append(newObjUrl) + parent.members.append(item) + return item + + def addImage(self , parent , image , title='' , description='' , name=''): + """ + Add a LocalImage to the parent album. + + parent(Album) : The parent album to add the image to + image(LocalImage) : The local image to upload and add to the + parent + title(str) : The image title + description(str) : The image description + name(str) : The image file name + + returns(RemoteImage) : The RemoteImage instance for the item + uploaded + """ + if not parent.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + parent.title) + if name: + image.Filename = name + entity = { + 'name': image.filename , + 'type': image.type , + 'title': title , + 'description': description , + } + boundary = str(uuid4()) + headers = {'Content-Type': 'multipart/form-data; boundary=%s' % + boundary} + # this is more complicated than adding an album. We have to + # construct the upload MIME headers, including build the string + # data section + data = '--%s\r\n' % boundary + data += 'Content-Disposition: form-data; name="entity"\r\n' + data += 'Content-Type: text/plain; ' \ + 'charset=UTF-8\r\n' + data += 'Content-Transfer-Encoding: 8bit\r\n' + data += '\r\n' + data += '%s\r\n' % json.dumps(entity , separators=(',' , ':')) + data += '--%s\r\n' % boundary + data += image.getUploadContent() + data += '--%s--\r\n' % boundary + req = PostRequest(parent.url , self.apiKey , data , headers) + resp = self._openReq(req) + newObjUrl = self._getUrlFromResp(resp) + item = getItemFromResp(self.getRespFromUrl(newObjUrl) , self , parent) + parent._members.append(newObjUrl) + parent.members.append(item) + return item + + def addMovie(self , parent , movie , title='' , description='' , name=''): + """ + Add a LocalMovie to the parent album. + + parent(Album) : The parent album to add the movie to + image(LocalMovie) : The local movie to upload and add to the + parent + title(str) : The movie title + description(str) : The movie description + name(str) : The movie file name + + returns(RemoteMovie) : The RemoteMovie instance for the movie + uploaded + """ + return self.addImage(parent , movie , title , description , name) + + def setAlbumCover(self , album , image): + """ + Updates a remote item's title and description + + album(Album) : The album to set the cover on + image(RemoteImage) : The image to use as the cover + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if not album.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + album.title) + try: + self._isItemValid(album , Album) + self._isItemValid(image , RemoteImage) + except Exception , e: + return (False , str(e)) + data = { + 'album_cover': image.url , + } + req = PutRequest(album.url , self.apiKey , data) + try: + resp = self._openReq(req) + except G3RequestError , e: + return (False , str(e)) + album.album_cover = image + album._album_cover = image.url + return (True , '') + + def updateItem(self , item): + """ + Updates a remote item's title and description + + item(BaseRemote) : An item descended from BaseRemote + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if not item.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + item.title) + try: + self._isItemValid(item , BaseRemote) + except Exception , e: + return (False , str(e)) + data = { + 'title': item.title , + 'description': item.description , + } + req = PutRequest(item.url , self.apiKey , data) + try: + resp = self._openReq(req) + except G3RequestError , e: + return (False , str(e)) + return (True , '') + + def updateAlbum(self , album): + """ + Update the title and description for an album. + + image(Album) : Updates the title and/or description + for the Album + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self.updateItem(album) + + def updateImage(self , image): + """ + Update the title and description for an image. + + image(RemoteImage) : Updates the title and/or description for the + RemoteImage + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self.updateItem(image) + + def updateMovie(self , movie): + """ + Update the title and description for a movie. + + image(RemoteMovie) : Updates the title and/or description for the + RemoteMovie + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + return self.updateItem(movie) + + def deleteItem(self , item): + """ + Deletes the given item. Item must be descended from BaseRemote. + + item(BaseRemote) : The item to delete + + returns(tuple(status , msg)) : Returns a tuple of a boolean status + and a message if there is an error + """ + if not item.can_edit: + raise G3AuthError('You do not have permission to edit: %s' % + item.title) + try: + self._isItemValid(item , BaseRemote) + except Exception , e: + return (False , e.message) + req = DeleteRequest(item.url , self.apiKey) + try: + resp = self._openReq(req) + except G3RequestError , e: + return (False , e.message) + return (True , '') + + def tagItem(self , item , tagName): + """ + Tag this item with the string "tagName" + + tagName(str) : The actual tag name + + returns(Tag) : The tag that was created + """ + # First we have to create the tag itself, if necessary + data = { + 'name': str(tagName) , + } + url = self._buildUrl('index.php/rest/tags') + req = PostRequest(url , self.apiKey , data) + resp = self._openReq(req) + r = json.loads(resp.read()) + tagUrl = r['url'] + # And now that we have our (possibly) newly created tag, we can + # use that to tag our item + data = { + 'tag': tagUrl , + 'item': item.url , + } + url = self._buildUrl('index.php/rest/item_tags/%s' % item.id) + req = PostRequest(url , self.apiKey , data) + resp = self._openReq(req) + respObj = json.loads(resp.read()) + item.relationships['tags']['members'].append(respObj['url']) + tag = Tag(respObj , self , item) + if hasattr(item , 'tags'): + item.tags.append(tag) + return tag + + def addComment(self , image , comment): + """ + Comment on this item with the string "comment" + + comment(str) : The comment + + returns(Comment) : The comment that was created + """ + data = { + 'item': image.url , + 'text': comment , + } + url = self._buildUrl('index.php/rest/comments') + req = PostRequest(url , self.apiKey , data) + resp = self._openReq(req) + commUrl = json.loads(resp.read())['url'] + resp = self.getRespFromUrl(commUrl) + comm = getItemFromResp(resp , self , image) + if hasattr(image , 'comments'): + image.comments.append(comm) + return comm + + def _buildOpener(self): + cp = urllib2.HTTPCookieProcessor() + self._opener = urllib2.build_opener(cp) + if self.ssl: + self._opener.add_handler(urllib2.HTTPSHandler()) + + def _buildUrl(self , resource , kwargs={}): + url = '%s://%s:%d/%s/%s' % (self.protocol , self.host , self.port , + quote(self.g3Base) , quote(resource)) + if kwargs: + url += '?%s' % urlencode(kwargs) + return url + + def _getUrlFromResp(self , resp): + d = json.loads(resp.read()) + return d['url'] + + def _openReq(self , req): + try: + resp = self._opener.open(req) + except urllib2.HTTPError , e: + err = json.loads(e.read()) + if isinstance(err , dict) and 'errors' in err: + raise G3RequestError(err['errors']) + else: + raise G3UnknownError('Unknown request error: %s' % e) + return resp + + def _isItemValid(self , item , cls): + if not isinstance(item , cls): + raise TypeError('Items to be modified must be descended from ' + '%s: %s' % (cls , type(item))) + if not 'url' in item.__dict__: + raise G3UnknownError('The object, %s, has no "url"' % item) + +def login(host , username , passwd , g3Base='/gallery3' , port=80 , + ssl=False): + """ + This will log you in and return a Gallery3 object on success, None + otherwise + + host(str) : The hostname of the gallery site + username(str) : The username to login with + passwd(str) : The password to login with + g3Base(str) : The remote url path to your gallery 3 install + (default: /gallery3) + port(int) : The port number to connect to (default: 80) + ssl(bool) : If true, use SSL for the connection (default: 80) + """ + data = { + 'user': username , + 'password': passwd , + } + protocol = ('http' , 'https')[ssl] + url = '%s://%s:%d/%s/index.php/rest' % (protocol , host , port , + quote(g3Base)) + req = PostRequest(url , None , urlencode(data)) + opener = urllib2.build_opener() + if ssl: + opener.add_handler(urllib2.HTTPSHandler()) + try: + resp = opener.open(req) + except urllib2.HTTPError , e: + return None + apiKey = resp.read().strip('\'"') + return Gallery3(host , apiKey , g3Base , port , ssl) diff --git a/3.0/client/Python/pylibgal3/libg3/Requests.py b/3.0/client/Python/pylibgal3/libg3/Requests.py new file mode 100644 index 00000000..7b0fedb6 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/Requests.py @@ -0,0 +1,72 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +__all__ = ['BaseRequest' , 'GetRequest' , 'PostRequest' , 'PutRequest' , + 'DeleteRequest'] + +from urllib2 import Request +from urllib import quote +import os , json , types + +class BaseRequest(Request): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + if apiKey is not None: + headers['X-Gallery-Request-Key'] = apiKey + if data is not None: + if isinstance(data , dict): + data = 'entity=%s' % quote(json.dumps(data , + separators=(',' , ':'))) + elif type(data) not in types.StringTypes: + raise TypeError('Invalid type for data. It should be ' + 'a "dict" or "str", not %s' % type(data)) + headers['Content-Length'] = str(len(data)) + Request.__init__(self , url , data , headers , origin_req_host , + unverifiable) + +class GetRequest(BaseRequest): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'get' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) + +class PostRequest(BaseRequest): + def __init__(self , url , apiKey , data , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'post' + if 'Content-Type' not in headers: + headers['Content-Type'] = 'application/x-www-form-urlencoded' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) + +class PutRequest(BaseRequest): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'put' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) + +class DeleteRequest(BaseRequest): + def __init__(self , url , apiKey , data=None , headers={} , + origin_req_host=None , unverifiable=False): + headers['X-Gallery-Request-Method'] = 'delete' + BaseRequest.__init__(self , url , apiKey , data , headers , + origin_req_host , unverifiable) diff --git a/3.0/client/Python/pylibgal3/libg3/__init__.py b/3.0/client/Python/pylibgal3/libg3/__init__.py new file mode 100644 index 00000000..745ba345 --- /dev/null +++ b/3.0/client/Python/pylibgal3/libg3/__init__.py @@ -0,0 +1,24 @@ +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + +from G3Items import * +from Gallery3 import * + +__version__ = '0.1.3' diff --git a/3.0/client/Python/pylibgal3/setup.py b/3.0/client/Python/pylibgal3/setup.py new file mode 100644 index 00000000..ba9d1d82 --- /dev/null +++ b/3.0/client/Python/pylibgal3/setup.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# +# Author: Jay Deiman +# Email: admin@splitstreams.com +# +# This file is part of pylibgal3. +# +# pylibgal3 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 3 of the License, or +# (at your option) any later version. +# +# pylibgal3 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 pylibgal3. If not, see . +# + + +from distutils.core import setup +from libg3 import __version__ as version +import sys + +setup(name='pylibgal3' , + version=version , + author='Jay Deiman' , + author_email='admin@splitstreams.com' , + url='http://stuffivelearned.org' , + description='A library for accessing/manipulating a Gallery 3 install' , + packages=['libg3'] , + package_dir={'libg3': 'libg3'} , +) From a906f6840ce14087ab9668890b357298ecef5ccc Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 22 Dec 2010 15:10:27 -0700 Subject: [PATCH 162/300] Initial commit of bitly module. Installer and admin settings implemented. URL shortening method works but is not yet implemented for items. --- 3.1/modules/bitly/controllers/admin_bitly.php | 91 +++++++ 3.1/modules/bitly/helpers/bitly.php | 257 ++++++++++++++++++ 3.1/modules/bitly/helpers/bitly_event.php | 50 ++++ 3.1/modules/bitly/helpers/bitly_installer.php | 38 +++ 3.1/modules/bitly/helpers/bitly_theme.php | 24 ++ 3.1/modules/bitly/js/bitly.js | 8 + 3.1/modules/bitly/models/bitly.php | 29 ++ 3.1/modules/bitly/module.info | 3 + 3.1/modules/bitly/views/admin_bitly.html.php | 18 ++ 9 files changed, 518 insertions(+) create mode 100644 3.1/modules/bitly/controllers/admin_bitly.php create mode 100644 3.1/modules/bitly/helpers/bitly.php create mode 100644 3.1/modules/bitly/helpers/bitly_event.php create mode 100644 3.1/modules/bitly/helpers/bitly_installer.php create mode 100644 3.1/modules/bitly/helpers/bitly_theme.php create mode 100644 3.1/modules/bitly/js/bitly.js create mode 100644 3.1/modules/bitly/models/bitly.php create mode 100644 3.1/modules/bitly/module.info create mode 100644 3.1/modules/bitly/views/admin_bitly.html.php diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php new file mode 100644 index 00000000..ee7e9f6f --- /dev/null +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -0,0 +1,91 @@ +validate()) { + $current_login = module::get_var("bitly", "login"); + $current_key = module::get_var("bitly", "api_key"); + $current_domain = module::get_var("bitly", "domain"); + $new_login = $form->configure_bitly->login->value; + $new_key = $form->configure_bitly->api_key->value; + $new_domain = $form->configure_bitly->domain->value; + + module::set_var("bitly", "login", $new_login); + module::set_var("bitly", "api_key", $new_key); + module::set_var("bitly", "domain", $new_domain); + + if (!bitly::check_config()) { + url::redirect("admin/bitly"); + } else { + if ($current_login && !$new_login) { + message::success(t("Your bit.ly login has been cleared.")); + } else if ($current_login && $new_login && $current_login != $new_login) { + message::success(t("Your bit.ly login has been changed.")); + } else if (!$current_login && $new_login) { + message::success(t("Your bit.ly login has been saved.")); + } + if ($current_key && !$new_key) { + message::success(t("Your bit.ly API key has been cleared.")); + } else if ($current_key && $new_key && $current_key != $new_key) { + message::success(t("Your bit.ly API key has been changed.")); + } else if (!$current_key && $new_key) { + message::success(t("Your bit.ly API key has been saved.")); + } + if ($current_domain && $new_domain && $current_domain != $new_domain) { + message::success(t("Your preferrend bit.ly domain has been changed.")); + } else if (!$current_domain && $new_domain) { + message::success(t("Your preferred bit.ly domain has been set.")); + } + log::success("bitly", t("bit.ly login changed to %new_login", + array("new_login" => $new_login))); + log::success("bitly", t("bit.ly API key changed to %new_key", + array("new_key" => $new_key))); + + (!$new_login || !$new_key) ? $valid_config = false : $valid_config = true; + } + } + } + + $view = new Admin_View("admin.html"); + $view->page_title = t("bit.ly url shortner"); + $view->content = new View("admin_bitly.html"); + $view->content->login = $form->configure_bitly->login->value; + $view->content->api_key = $form->configure_bitly->api_key->value; + $view->content->domain = $form->configure_bitly->domain->value; + $view->content->valid_config = $valid_config; + $view->content->form = $form; + if ($valid_config) { + // @todo Store/get G3's root bit.ly url, only shorten if it hasn't been stored. + $view->content->g3_url = bitly::shorten_url(bitly::build_link()); + } + print $view; + } + +} \ No newline at end of file diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php new file mode 100644 index 00000000..b69f7fe1 --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly.php @@ -0,0 +1,257 @@ + 'expand', + 'shorten' => 'shorten', + 'validate' => 'validate', + 'clicks' => 'clicks', + 'referrers' => 'referrers', + 'countries' => 'countries', + 'clicks_by_minute' => 'clicks_by_minute', + 'clicks_by_day' => 'clicks_by_day', + 'lookup' => 'lookup', + 'info' => 'info', + ); + + static function get_configure_form() { + $form = new Forge("admin/bitly", "", "post", array("id" => "g-configure-bitly-form")); + $group = $form->group("configure_bitly")->label(t("Configure bit.ly")); + $group->input("login") + ->label(t("Login")) + ->value(module::get_var("bitly", "login")) + ->rules("required") + ->error_messages("required", t("You must enter a login")); + $group->input("api_key") + ->label(t("API Key")) + ->value(module::get_var("bitly", "api_key")) + ->rules("required") + ->error_messages("required", t("You must enter an API key")); + $group->dropdown("domain") + ->label(t("Preferred Domain")) + ->options(array("bit.ly" => "bit.ly", "j.mp" => "j.mp")) + ->selected(module::get_var("bitly", "domain")); + $group->submit("")->value(t("Save")); + return $form; + } + + /** + * Check a login and an API Key against bit.ly to make sure they're valid + * @param string $login the login + * @param string $api_key the API key + * @return boolean + */ + static function validate_config($login, $api_key) { + if (!empty($login) && !empty($api_key)) { + $parameters = array( + 'login' => $login, + 'apiKey' => $api_key, + 'x_login' => $login, + 'x_apiKey' => $api_key + ); + $request = self::_build_http_request('validate', $parameters); + $response = self::_http_post($request, "api.bit.ly"); + $json_decoded = json_decode($response->body[0]); + if (!$json_decoded->data->valid) { + if ("INVALID_LOGIN" == $json_decoded->status_txt) { + message::error(t("Your bit.ly login is incorrect")); + } else if ("INVALID_APIKEY" == $json_decoded->status_txt) { + message::error(t("Your bit.ly API Key is incorrect.")); + } + return false; + } else { + return true; + } + } + } + + /** + * Check whether the module's configured correctly + * @return boolean + */ + static function check_config() { + $login = module::get_var("bitly", "login"); + $api_key = module::get_var("bitly", "api_key"); + if (empty($login) || empty($api_key)) { + site_status::warning( + t("bit.ly is not quite ready! Please provide a login and API Key", + array("url" => html::mark_clean(url::site("admin/bitly")))), + "bitly_config"); + + } else if (!self::validate_config($login, $api_key)) { + site_status::warning( + t("bit.ly is not properly configured! URLs will not be shortened until its configuration is updated.", + array("url" => html::mark_clean(url::site("admin/bitly")))), + "bitly_config"); + } else { + site_status::clear("bitly_config"); + return true; + } + return false; + } + + /** + * + * @param $type + * @param $parameters + * @return string + */ + private static function _build_http_request($type, $parameters) { + $http_request = ''; + if (!empty($type) && count($parameters)) { + foreach($parameters as $k => $v) { + $query_string[] = "$k=" . urlencode($v); + } + $path = "/" . self::$api_version . "/$type?" . implode('&', $query_string); + $module_version = module::get_version("bitly"); + + $http_request = "GET $path HTTP/1.0\r\n"; + $http_request .= "Host: " . self::$api_host . "\r\n"; + $http_request .= "User-Agent: Gallery/3 | bitly/" . module::get_version("bitly") . "\r\n"; + $http_request .= "\r\n"; + $http_request .= $path; + } + return $http_request; + } + + /** + * Send an http POST request + * @param string $http_request + * @param string $host + * @return object + */ + private static function _http_post($http_request) { + $response = ''; + //Kohana_Log::add("debug", "Send request\n" . print_r($http_request, 1)); + if (false !== ($fs = @fsockopen(self::$api_host, 80, $errno, $errstr, 5))) { + fwrite($fs, $http_request); + while ( !feof($fs) ) { + $response .= fgets($fs, 1160); // One TCP-IP packet + } + fclose($fs); + list($headers, $body) = explode("\r\n\r\n", $response); + $headers = explode("\r\n", $headers); + $body = explode("\r\n", $body); + $response = new ArrayObject( + array("headers" => $headers, "body" => $body), ArrayObject::ARRAY_AS_PROPS); + } else { + throw new Exception("@todo CONNECTION TO URL SHORTENING SERVICE FAILED"); + } + Kohana_Log::add("debug", "Received response\n" . print_r($response, 1)); + + return $response; + } + + static function build_link($path='/') { + $protocol = strtolower(substr($_SERVER["SERVER_PROTOCOL"], 0, 5)) == 'https' ? 'https' : 'http'; + return url::site($path, $protocol); + } + + /** + * Shorten a Gallery URL + * @param string $long_url + * @param string $format + * @return string + */ + static function shorten_url($long_url, $format='json') { + $short_url = ''; + $parameters = array( + "login" => module::get_var("bitly", "login"), + 'apiKey' => module::get_var("bitly", "api_key"), + 'longUrl' => $long_url, + 'domain' => module::get_var("bitly", "domain"), + 'format' => $format, + ); + $request = self::_build_http_request('shorten', $parameters); + $response = self::_http_post($request, self::$api_host); + $json_decoded = json_decode($response->body[0]); + if ('OK' == $json_decoded->status_txt) { + // Save the original item id, the hash, and possibly the global hash, to the database + $short_url = $json_decoded->data->url; + $hash = $json_decoded->data->hash; + $global_hash = $json_decoded->data->global_hash; + $new_hash = $json_decoded->data->new_hash; + message::success("The $long_url has been shortened to $short_url"); + } else { + message::error("Unable to shorten the url"); + // @todo log the error + } + return $short_url; + } + + /** + * returns expanded url + * { + "status_code": 200, + "data": { + "expand": [ + { + "short_url": "http://tcrn.ch/a4MSUH", + "global_hash": "bWw49z", + "long_url": "http://www.techcrunch.com/2010/01/29/windows-mobile-foursquare/", + "user_hash": "a4MSUH" + }, + { + "short_url": "http://bit.ly/1YKMfY", + "global_hash": "1YKMfY", + "long_url": "http://betaworks.com/", + "user_hash": "1YKMfY" + }, + { + "long_url": "http://www.scotster.com/qf/?1152", + "global_hash": "lLWr", + "hash": "j3", + "user_hash": "j3" + }, + { + "hash": "a35.", + "error": "NOT_FOUND" + } + ] + }, + "status_txt": "OK" + } + */ + /** + * + * @param $short_url + * @param $hash + * @param $format + * @return + */ + static function expand_url($short_url, $hash=null, $format='json') { + $parameters = array( + "login" => module::get_var("bitly", "login"), + 'apiKey' => module::get_var("bitly", "api_key"), + 'shortUrl' => $short_url, + 'hash' => $hash, + ); + $request = self::_build_http_request('expand', $parameters); + $response = self::_http_post($http_request, self::$api_host); + return $response; + } + +} diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php new file mode 100644 index 00000000..49ea72a6 --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -0,0 +1,50 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("bitly_menu") + ->label(t("Bit.ly")) + ->url(url::site("admin/bitly"))); + } + + static function site_menu($menu, $theme) { + $item = $theme->item(); + if ($item && $item->type == "photo") { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten Link with bit.ly")) + ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) + ->css_id("g-bitly-link") + ->css_class("g-print-bitly-link ui-icon-print")); + } + } + + static function context_menu($menu, $theme, $item) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten Link with bit.ly")) + ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) + ->css_class("g-bitly-link ui-icon-link")); + } +} diff --git a/3.1/modules/bitly/helpers/bitly_installer.php b/3.1/modules/bitly/helpers/bitly_installer.php new file mode 100644 index 00000000..c847ec7c --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly_installer.php @@ -0,0 +1,38 @@ +query("CREATE TABLE {bitly_urls} ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `item_id` int(9) NOT NULL, + `hash` char(6) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("bitly", 1); + bitly::check_config(); + } + + static function deactivate() { + site_status::clear("bitly_config"); + } +} diff --git a/3.1/modules/bitly/helpers/bitly_theme.php b/3.1/modules/bitly/helpers/bitly_theme.php new file mode 100644 index 00000000..41aba38f --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly_theme.php @@ -0,0 +1,24 @@ +script("bitly.js"); + } +} diff --git a/3.1/modules/bitly/js/bitly.js b/3.1/modules/bitly/js/bitly.js new file mode 100644 index 00000000..35bcd58b --- /dev/null +++ b/3.1/modules/bitly/js/bitly.js @@ -0,0 +1,8 @@ +$(document).ready(function() { + $(".g-bitly-link").click(function(e) { + e.preventDefault(); + return; + }); +}); + + diff --git a/3.1/modules/bitly/models/bitly.php b/3.1/modules/bitly/models/bitly.php new file mode 100644 index 00000000..a1b68600 --- /dev/null +++ b/3.1/modules/bitly/models/bitly.php @@ -0,0 +1,29 @@ + +
                      +

                      +

                      + bit.ly account which will provide an API Key, which is also free.", + array("api_key_url" => "http://bit.ly/a/your_api_key", + "bitly_url" => "http://bit.ly")) ?> +

                      +
                      + +
                      + %g3_url", array('g3_url' => $g3_url)) ?> +
                      + + + +
                      +
                      From 8b3379c231c09ed1f48b170413ef94eff8483ee0 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Thu, 23 Dec 2010 23:38:43 -0800 Subject: [PATCH 163/300] Mark the register controller as a legal controller when the entire Gallery is private. Fixes #1549. --- 3.0/modules/register/controllers/register.php | 2 ++ 3.1/modules/register/controllers/register.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/3.0/modules/register/controllers/register.php b/3.0/modules/register/controllers/register.php index 7f79bc63..c070e9ef 100755 --- a/3.0/modules/register/controllers/register.php +++ b/3.0/modules/register/controllers/register.php @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class register_Controller extends Controller { + const ALLOW_PRIVATE_GALLERY = true; + public function index() { print $this->_get_form(); } diff --git a/3.1/modules/register/controllers/register.php b/3.1/modules/register/controllers/register.php index 7f79bc63..c070e9ef 100755 --- a/3.1/modules/register/controllers/register.php +++ b/3.1/modules/register/controllers/register.php @@ -17,6 +17,8 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class register_Controller extends Controller { + const ALLOW_PRIVATE_GALLERY = true; + public function index() { print $this->_get_form(); } From 19436c2ebd410a5c3128c52ea979825f92305846 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Fri, 24 Dec 2010 11:22:23 -0700 Subject: [PATCH 164/300] Changed table name and user_id is now owner_id, as it should be. --- 3.1/modules/bitly/helpers/bitly_installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_installer.php b/3.1/modules/bitly/helpers/bitly_installer.php index c847ec7c..c55fbb66 100644 --- a/3.1/modules/bitly/helpers/bitly_installer.php +++ b/3.1/modules/bitly/helpers/bitly_installer.php @@ -21,11 +21,11 @@ class bitly_installer { static function install() { Database::instance() - ->query("CREATE TABLE {bitly_urls} ( + ->query("CREATE TABLE {bitly_links} ( `id` int(9) NOT NULL AUTO_INCREMENT, `item_id` int(9) NOT NULL, `hash` char(6) NOT NULL, - `user_id` int(9) NOT NULL, + `owner_id` int(9) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); module::set_version("bitly", 1); From 3b4f6f357e59663b38909f954e582a53a8683828 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Fri, 24 Dec 2010 15:04:42 -0700 Subject: [PATCH 165/300] Store shortened bit.ly hashes locally. Implemented site menu's shorten link, context menu's not working yet. --- 3.1/modules/bitly/controllers/admin_bitly.php | 9 +- 3.1/modules/bitly/helpers/bitly.php | 96 +++++-------------- 3.1/modules/bitly/helpers/bitly_event.php | 20 ++-- 3.1/modules/bitly/models/bitly.php | 29 ------ 4 files changed, 42 insertions(+), 112 deletions(-) delete mode 100644 3.1/modules/bitly/models/bitly.php diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php index ee7e9f6f..f4240a63 100644 --- a/3.1/modules/bitly/controllers/admin_bitly.php +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -81,9 +81,14 @@ class Admin_Bitly_Controller extends Admin_Controller { $view->content->domain = $form->configure_bitly->domain->value; $view->content->valid_config = $valid_config; $view->content->form = $form; + if ($valid_config) { - // @todo Store/get G3's root bit.ly url, only shorten if it hasn't been stored. - $view->content->g3_url = bitly::shorten_url(bitly::build_link()); + $link = ORM::factory("bitly_link")->where("item_id", "=", 1)->find(); + if ($link->loaded()) { + $view->content->g3_url = "http://" . module::get_var("bitly", "domain") . "/$link->hash"; + } else { + $view->content->g3_url = bitly::shorten_url(1); + } } print $view; } diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index b69f7fe1..276c7796 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -165,19 +165,17 @@ class bitly_Core { return $response; } - static function build_link($path='/') { - $protocol = strtolower(substr($_SERVER["SERVER_PROTOCOL"], 0, 5)) == 'https' ? 'https' : 'http'; - return url::site($path, $protocol); - } - /** * Shorten a Gallery URL - * @param string $long_url + * @param int $item_id * @param string $format - * @return string + * @return mixed string|false */ - static function shorten_url($long_url, $format='json') { + static function shorten_url($item_id, $format='json') { + $item = ORM::factory("item", $item_id); $short_url = ''; + $long_url = url::abs_site($item->relative_url_cache); + $parameters = array( "login" => module::get_var("bitly", "login"), 'apiKey' => module::get_var("bitly", "api_key"), @@ -185,73 +183,31 @@ class bitly_Core { 'domain' => module::get_var("bitly", "domain"), 'format' => $format, ); + $request = self::_build_http_request('shorten', $parameters); $response = self::_http_post($request, self::$api_host); - $json_decoded = json_decode($response->body[0]); - if ('OK' == $json_decoded->status_txt) { - // Save the original item id, the hash, and possibly the global hash, to the database - $short_url = $json_decoded->data->url; - $hash = $json_decoded->data->hash; - $global_hash = $json_decoded->data->global_hash; - $new_hash = $json_decoded->data->new_hash; - message::success("The $long_url has been shortened to $short_url"); + $json_response = json_decode($response->body[0]); + + if ('OK' == $json_response->status_txt) { + $short_url = $json_response->data->url; + // Save the link hash to the database + $link = ORM::factory("bitly_link"); + $link->item_id = $item_id; + $link->hash = $json_response->data->hash; + //$link->global_hash = $json_response->data->global_hash; + //$new_hash = $json_response->data->new_hash; + $link->owner_id = $item->owner_id; + $link->save(); + + message::success("$long_url has been shortened to $short_url"); + + return $json_response->data->url; + } else { - message::error("Unable to shorten the url"); + message::error("Unable to shorten $long_url"); // @todo log the error + return false; } - return $short_url; } - /** - * returns expanded url - * { - "status_code": 200, - "data": { - "expand": [ - { - "short_url": "http://tcrn.ch/a4MSUH", - "global_hash": "bWw49z", - "long_url": "http://www.techcrunch.com/2010/01/29/windows-mobile-foursquare/", - "user_hash": "a4MSUH" - }, - { - "short_url": "http://bit.ly/1YKMfY", - "global_hash": "1YKMfY", - "long_url": "http://betaworks.com/", - "user_hash": "1YKMfY" - }, - { - "long_url": "http://www.scotster.com/qf/?1152", - "global_hash": "lLWr", - "hash": "j3", - "user_hash": "j3" - }, - { - "hash": "a35.", - "error": "NOT_FOUND" - } - ] - }, - "status_txt": "OK" - } - */ - /** - * - * @param $short_url - * @param $hash - * @param $format - * @return - */ - static function expand_url($short_url, $hash=null, $format='json') { - $parameters = array( - "login" => module::get_var("bitly", "login"), - 'apiKey' => module::get_var("bitly", "api_key"), - 'shortUrl' => $short_url, - 'hash' => $hash, - ); - $request = self::_build_http_request('expand', $parameters); - $response = self::_http_post($http_request, self::$api_host); - return $response; - } - } diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 49ea72a6..3701df8e 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -28,15 +28,13 @@ class bitly_event_Core { static function site_menu($menu, $theme) { $item = $theme->item(); - if ($item && $item->type == "photo") { - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten Link with bit.ly")) - ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) - ->css_id("g-bitly-link") - ->css_class("g-print-bitly-link ui-icon-print")); - } + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten Link with bit.ly")) + ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->css_id("g-bitly-link") + ->css_class("g-bitly-shorten ui-icon-link")); } static function context_menu($menu, $theme, $item) { @@ -44,7 +42,7 @@ class bitly_event_Core { ->append(Menu::factory("link") ->id("bitly") ->label(t("Shorten Link with bit.ly")) - ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) - ->css_class("g-bitly-link ui-icon-link")); + ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->css_class("g-bitly-shorten ui-icon-link")); } } diff --git a/3.1/modules/bitly/models/bitly.php b/3.1/modules/bitly/models/bitly.php deleted file mode 100644 index a1b68600..00000000 --- a/3.1/modules/bitly/models/bitly.php +++ /dev/null @@ -1,29 +0,0 @@ - Date: Sat, 25 Dec 2010 15:24:15 -0700 Subject: [PATCH 166/300] Store shortened bit.ly hashes locally. Implemented site menu's shorten link, context menu's not working yet. (missed adding the controller in original commit) --- 3.1/modules/bitly/controllers/bitly.php | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 3.1/modules/bitly/controllers/bitly.php diff --git a/3.1/modules/bitly/controllers/bitly.php b/3.1/modules/bitly/controllers/bitly.php new file mode 100644 index 00000000..608918d3 --- /dev/null +++ b/3.1/modules/bitly/controllers/bitly.php @@ -0,0 +1,43 @@ +relative_url_cache)); + } + +} \ No newline at end of file From f6f66d01eb77783907d6b610a2fc2ceeb119bbdc Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 25 Dec 2010 15:37:32 -0700 Subject: [PATCH 167/300] Hooked up shorten link for contextual menu. --- 3.1/modules/bitly/js/bitly.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/3.1/modules/bitly/js/bitly.js b/3.1/modules/bitly/js/bitly.js index 35bcd58b..96dcb427 100644 --- a/3.1/modules/bitly/js/bitly.js +++ b/3.1/modules/bitly/js/bitly.js @@ -1,8 +1,6 @@ $(document).ready(function() { - $(".g-bitly-link").click(function(e) { + $(".g-bitly-shorten").click(function(e) { e.preventDefault(); - return; + return window.location = ($(this).attr("href")); }); }); - - From a192cb879aceacc654cae248c12813c5ed053f98 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Sat, 25 Dec 2010 18:18:47 -0600 Subject: [PATCH 168/300] Fixed large member access (over 100) --- 3.0/client/Python/pylibgal3/libg3/Gallery3.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/3.0/client/Python/pylibgal3/libg3/Gallery3.py b/3.0/client/Python/pylibgal3/libg3/Gallery3.py index 924d7d86..6345c1a8 100644 --- a/3.0/client/Python/pylibgal3/libg3/Gallery3.py +++ b/3.0/client/Python/pylibgal3/libg3/Gallery3.py @@ -95,11 +95,20 @@ class Gallery3(object): returns(list[BaseRemote]) : Returns a list of the corresponding remote objects """ - data = { - 'urls': json.dumps(urls) , - } - resp = self.getRespFromUri('index.php/rest/items' , data) - return getItemsFromResp(resp , self , parent) + numUrls = len(urls) + start = 0 + increment = 25 + ret = [] + while start < numUrls: + data = { + 'urls': json.dumps(urls[start:start+increment]) , + 'num': str(increment) , + 'start': str(start) , + } + resp = self.getRespFromUri('index.php/rest/items' , data) + ret.extend(getItemsFromResp(resp , self , parent)) + start += increment + return ret def getRespFromUrl(self , url): """ @@ -120,6 +129,7 @@ class Gallery3(object): uri(str) : The uri string defining the resource on the defined host """ url = self._buildUrl(uri , kwargs) + print url return self.getRespFromUrl(url) def addAlbum(self , parent , albumName , title , description=''): From 97ccfc9865791c27c750c9b99ac978511d581773 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Sat, 25 Dec 2010 18:20:12 -0600 Subject: [PATCH 169/300] Added resize and thumbnail access --- 3.0/client/Python/pylibgal3/libg3/G3Items.py | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/3.0/client/Python/pylibgal3/libg3/G3Items.py b/3.0/client/Python/pylibgal3/libg3/G3Items.py index f6477bb8..0fdae593 100644 --- a/3.0/client/Python/pylibgal3/libg3/G3Items.py +++ b/3.0/client/Python/pylibgal3/libg3/G3Items.py @@ -36,6 +36,13 @@ class BaseRemote(object): self.fh = None self._postInit() + def __str__(self): + try: + return self.title + except: + pass + return self.name + def __getattr__(self , name): """ A bit of magic to make the retrieval of member objects lazy @@ -366,6 +373,32 @@ class RemoteImage(BaseRemote , Image): except: pass + def getResizeHandle(self): + """ + Returns a file-like object (specifically a urllib2.addinfourl) handle + to the "resize" version of the image + + returns(urllib2.addinfourl) : A file-like object handle for retrieving + the resized image + """ + if hasattr(self , 'resize_url'): + resp = self._gal.getRespFromUrl(self.resize_url) + return resp + return None + + def getThumbHandle(self): + """ + Returns a file-like object (specifically a urllib2.addinfourl) handle + to the "thumbnail" version of the image + + returns(urllib2.addinfourl) : A file-like object handle for retrieving + the thumbnail image + """ + if hasattr(self , 'thumb_url'): + resp = self._gal.getRespFromUrl(self.thumb_url) + return resp + return None + class LocalMovie(LocalImage): def __init__(self , path , replaceSpaces=True): LocalImage.__init__(self , path , replaceSpaces) From 093c4e7998be549669aaaadff012a9ef6dd33226 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Sat, 25 Dec 2010 18:20:58 -0600 Subject: [PATCH 170/300] version bump and updated issues --- 3.0/client/Python/pylibgal3/KNOWN_ISSUES | 1 - 3.0/client/Python/pylibgal3/libg3/__init__.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/3.0/client/Python/pylibgal3/KNOWN_ISSUES b/3.0/client/Python/pylibgal3/KNOWN_ISSUES index 7cad5280..ebc02f79 100644 --- a/3.0/client/Python/pylibgal3/KNOWN_ISSUES +++ b/3.0/client/Python/pylibgal3/KNOWN_ISSUES @@ -2,7 +2,6 @@ pylibgal3 ========= * Need to implement member sorting -* Need to implement thumbnail access and manipulation * Need to implement image resizing * The getRandomImage() does not currently work diff --git a/3.0/client/Python/pylibgal3/libg3/__init__.py b/3.0/client/Python/pylibgal3/libg3/__init__.py index 745ba345..eec91668 100644 --- a/3.0/client/Python/pylibgal3/libg3/__init__.py +++ b/3.0/client/Python/pylibgal3/libg3/__init__.py @@ -21,4 +21,4 @@ from G3Items import * from Gallery3 import * -__version__ = '0.1.3' +__version__ = '0.1.4' From 0e65453cf133ed59f1220ae661a09850f8d32131 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Sun, 26 Dec 2010 08:18:47 +0800 Subject: [PATCH 171/300] Fixed large member access (over 100) --- 3.0/client/Python/pylibgal3/libg3/Gallery3.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/3.0/client/Python/pylibgal3/libg3/Gallery3.py b/3.0/client/Python/pylibgal3/libg3/Gallery3.py index 924d7d86..6345c1a8 100644 --- a/3.0/client/Python/pylibgal3/libg3/Gallery3.py +++ b/3.0/client/Python/pylibgal3/libg3/Gallery3.py @@ -95,11 +95,20 @@ class Gallery3(object): returns(list[BaseRemote]) : Returns a list of the corresponding remote objects """ - data = { - 'urls': json.dumps(urls) , - } - resp = self.getRespFromUri('index.php/rest/items' , data) - return getItemsFromResp(resp , self , parent) + numUrls = len(urls) + start = 0 + increment = 25 + ret = [] + while start < numUrls: + data = { + 'urls': json.dumps(urls[start:start+increment]) , + 'num': str(increment) , + 'start': str(start) , + } + resp = self.getRespFromUri('index.php/rest/items' , data) + ret.extend(getItemsFromResp(resp , self , parent)) + start += increment + return ret def getRespFromUrl(self , url): """ @@ -120,6 +129,7 @@ class Gallery3(object): uri(str) : The uri string defining the resource on the defined host """ url = self._buildUrl(uri , kwargs) + print url return self.getRespFromUrl(url) def addAlbum(self , parent , albumName , title , description=''): From 530215e2b5686df9c3c92cebe4a7f13dd7ec64d7 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Sun, 26 Dec 2010 08:20:12 +0800 Subject: [PATCH 172/300] Added resize and thumbnail access --- 3.0/client/Python/pylibgal3/libg3/G3Items.py | 33 ++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/3.0/client/Python/pylibgal3/libg3/G3Items.py b/3.0/client/Python/pylibgal3/libg3/G3Items.py index f6477bb8..0fdae593 100644 --- a/3.0/client/Python/pylibgal3/libg3/G3Items.py +++ b/3.0/client/Python/pylibgal3/libg3/G3Items.py @@ -36,6 +36,13 @@ class BaseRemote(object): self.fh = None self._postInit() + def __str__(self): + try: + return self.title + except: + pass + return self.name + def __getattr__(self , name): """ A bit of magic to make the retrieval of member objects lazy @@ -366,6 +373,32 @@ class RemoteImage(BaseRemote , Image): except: pass + def getResizeHandle(self): + """ + Returns a file-like object (specifically a urllib2.addinfourl) handle + to the "resize" version of the image + + returns(urllib2.addinfourl) : A file-like object handle for retrieving + the resized image + """ + if hasattr(self , 'resize_url'): + resp = self._gal.getRespFromUrl(self.resize_url) + return resp + return None + + def getThumbHandle(self): + """ + Returns a file-like object (specifically a urllib2.addinfourl) handle + to the "thumbnail" version of the image + + returns(urllib2.addinfourl) : A file-like object handle for retrieving + the thumbnail image + """ + if hasattr(self , 'thumb_url'): + resp = self._gal.getRespFromUrl(self.thumb_url) + return resp + return None + class LocalMovie(LocalImage): def __init__(self , path , replaceSpaces=True): LocalImage.__init__(self , path , replaceSpaces) From d6b753b8f604a0f895fa4f10996a1569d6f33ce0 Mon Sep 17 00:00:00 2001 From: Jay Deiman Date: Sun, 26 Dec 2010 08:20:58 +0800 Subject: [PATCH 173/300] version bump and updated issues --- 3.0/client/Python/pylibgal3/KNOWN_ISSUES | 1 - 3.0/client/Python/pylibgal3/libg3/__init__.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/3.0/client/Python/pylibgal3/KNOWN_ISSUES b/3.0/client/Python/pylibgal3/KNOWN_ISSUES index 7cad5280..ebc02f79 100644 --- a/3.0/client/Python/pylibgal3/KNOWN_ISSUES +++ b/3.0/client/Python/pylibgal3/KNOWN_ISSUES @@ -2,7 +2,6 @@ pylibgal3 ========= * Need to implement member sorting -* Need to implement thumbnail access and manipulation * Need to implement image resizing * The getRandomImage() does not currently work diff --git a/3.0/client/Python/pylibgal3/libg3/__init__.py b/3.0/client/Python/pylibgal3/libg3/__init__.py index 745ba345..eec91668 100644 --- a/3.0/client/Python/pylibgal3/libg3/__init__.py +++ b/3.0/client/Python/pylibgal3/libg3/__init__.py @@ -21,4 +21,4 @@ from G3Items import * from Gallery3 import * -__version__ = '0.1.3' +__version__ = '0.1.4' From 9fc5a87ec25d7133a70bd974af929e42a52ae8c0 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Thu, 23 Dec 2010 06:10:27 +0800 Subject: [PATCH 174/300] Initial commit of bitly module. Installer and admin settings implemented. URL shortening method works but is not yet implemented for items. --- 3.1/modules/bitly/controllers/admin_bitly.php | 91 +++++++ 3.1/modules/bitly/helpers/bitly.php | 257 ++++++++++++++++++ 3.1/modules/bitly/helpers/bitly_event.php | 50 ++++ 3.1/modules/bitly/helpers/bitly_installer.php | 38 +++ 3.1/modules/bitly/helpers/bitly_theme.php | 24 ++ 3.1/modules/bitly/js/bitly.js | 8 + 3.1/modules/bitly/models/bitly.php | 29 ++ 3.1/modules/bitly/module.info | 3 + 3.1/modules/bitly/views/admin_bitly.html.php | 18 ++ 9 files changed, 518 insertions(+) create mode 100644 3.1/modules/bitly/controllers/admin_bitly.php create mode 100644 3.1/modules/bitly/helpers/bitly.php create mode 100644 3.1/modules/bitly/helpers/bitly_event.php create mode 100644 3.1/modules/bitly/helpers/bitly_installer.php create mode 100644 3.1/modules/bitly/helpers/bitly_theme.php create mode 100644 3.1/modules/bitly/js/bitly.js create mode 100644 3.1/modules/bitly/models/bitly.php create mode 100644 3.1/modules/bitly/module.info create mode 100644 3.1/modules/bitly/views/admin_bitly.html.php diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php new file mode 100644 index 00000000..ee7e9f6f --- /dev/null +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -0,0 +1,91 @@ +validate()) { + $current_login = module::get_var("bitly", "login"); + $current_key = module::get_var("bitly", "api_key"); + $current_domain = module::get_var("bitly", "domain"); + $new_login = $form->configure_bitly->login->value; + $new_key = $form->configure_bitly->api_key->value; + $new_domain = $form->configure_bitly->domain->value; + + module::set_var("bitly", "login", $new_login); + module::set_var("bitly", "api_key", $new_key); + module::set_var("bitly", "domain", $new_domain); + + if (!bitly::check_config()) { + url::redirect("admin/bitly"); + } else { + if ($current_login && !$new_login) { + message::success(t("Your bit.ly login has been cleared.")); + } else if ($current_login && $new_login && $current_login != $new_login) { + message::success(t("Your bit.ly login has been changed.")); + } else if (!$current_login && $new_login) { + message::success(t("Your bit.ly login has been saved.")); + } + if ($current_key && !$new_key) { + message::success(t("Your bit.ly API key has been cleared.")); + } else if ($current_key && $new_key && $current_key != $new_key) { + message::success(t("Your bit.ly API key has been changed.")); + } else if (!$current_key && $new_key) { + message::success(t("Your bit.ly API key has been saved.")); + } + if ($current_domain && $new_domain && $current_domain != $new_domain) { + message::success(t("Your preferrend bit.ly domain has been changed.")); + } else if (!$current_domain && $new_domain) { + message::success(t("Your preferred bit.ly domain has been set.")); + } + log::success("bitly", t("bit.ly login changed to %new_login", + array("new_login" => $new_login))); + log::success("bitly", t("bit.ly API key changed to %new_key", + array("new_key" => $new_key))); + + (!$new_login || !$new_key) ? $valid_config = false : $valid_config = true; + } + } + } + + $view = new Admin_View("admin.html"); + $view->page_title = t("bit.ly url shortner"); + $view->content = new View("admin_bitly.html"); + $view->content->login = $form->configure_bitly->login->value; + $view->content->api_key = $form->configure_bitly->api_key->value; + $view->content->domain = $form->configure_bitly->domain->value; + $view->content->valid_config = $valid_config; + $view->content->form = $form; + if ($valid_config) { + // @todo Store/get G3's root bit.ly url, only shorten if it hasn't been stored. + $view->content->g3_url = bitly::shorten_url(bitly::build_link()); + } + print $view; + } + +} \ No newline at end of file diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php new file mode 100644 index 00000000..b69f7fe1 --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly.php @@ -0,0 +1,257 @@ + 'expand', + 'shorten' => 'shorten', + 'validate' => 'validate', + 'clicks' => 'clicks', + 'referrers' => 'referrers', + 'countries' => 'countries', + 'clicks_by_minute' => 'clicks_by_minute', + 'clicks_by_day' => 'clicks_by_day', + 'lookup' => 'lookup', + 'info' => 'info', + ); + + static function get_configure_form() { + $form = new Forge("admin/bitly", "", "post", array("id" => "g-configure-bitly-form")); + $group = $form->group("configure_bitly")->label(t("Configure bit.ly")); + $group->input("login") + ->label(t("Login")) + ->value(module::get_var("bitly", "login")) + ->rules("required") + ->error_messages("required", t("You must enter a login")); + $group->input("api_key") + ->label(t("API Key")) + ->value(module::get_var("bitly", "api_key")) + ->rules("required") + ->error_messages("required", t("You must enter an API key")); + $group->dropdown("domain") + ->label(t("Preferred Domain")) + ->options(array("bit.ly" => "bit.ly", "j.mp" => "j.mp")) + ->selected(module::get_var("bitly", "domain")); + $group->submit("")->value(t("Save")); + return $form; + } + + /** + * Check a login and an API Key against bit.ly to make sure they're valid + * @param string $login the login + * @param string $api_key the API key + * @return boolean + */ + static function validate_config($login, $api_key) { + if (!empty($login) && !empty($api_key)) { + $parameters = array( + 'login' => $login, + 'apiKey' => $api_key, + 'x_login' => $login, + 'x_apiKey' => $api_key + ); + $request = self::_build_http_request('validate', $parameters); + $response = self::_http_post($request, "api.bit.ly"); + $json_decoded = json_decode($response->body[0]); + if (!$json_decoded->data->valid) { + if ("INVALID_LOGIN" == $json_decoded->status_txt) { + message::error(t("Your bit.ly login is incorrect")); + } else if ("INVALID_APIKEY" == $json_decoded->status_txt) { + message::error(t("Your bit.ly API Key is incorrect.")); + } + return false; + } else { + return true; + } + } + } + + /** + * Check whether the module's configured correctly + * @return boolean + */ + static function check_config() { + $login = module::get_var("bitly", "login"); + $api_key = module::get_var("bitly", "api_key"); + if (empty($login) || empty($api_key)) { + site_status::warning( + t("bit.ly is not quite ready! Please provide a login and API Key", + array("url" => html::mark_clean(url::site("admin/bitly")))), + "bitly_config"); + + } else if (!self::validate_config($login, $api_key)) { + site_status::warning( + t("bit.ly is not properly configured! URLs will not be shortened until its configuration is updated.", + array("url" => html::mark_clean(url::site("admin/bitly")))), + "bitly_config"); + } else { + site_status::clear("bitly_config"); + return true; + } + return false; + } + + /** + * + * @param $type + * @param $parameters + * @return string + */ + private static function _build_http_request($type, $parameters) { + $http_request = ''; + if (!empty($type) && count($parameters)) { + foreach($parameters as $k => $v) { + $query_string[] = "$k=" . urlencode($v); + } + $path = "/" . self::$api_version . "/$type?" . implode('&', $query_string); + $module_version = module::get_version("bitly"); + + $http_request = "GET $path HTTP/1.0\r\n"; + $http_request .= "Host: " . self::$api_host . "\r\n"; + $http_request .= "User-Agent: Gallery/3 | bitly/" . module::get_version("bitly") . "\r\n"; + $http_request .= "\r\n"; + $http_request .= $path; + } + return $http_request; + } + + /** + * Send an http POST request + * @param string $http_request + * @param string $host + * @return object + */ + private static function _http_post($http_request) { + $response = ''; + //Kohana_Log::add("debug", "Send request\n" . print_r($http_request, 1)); + if (false !== ($fs = @fsockopen(self::$api_host, 80, $errno, $errstr, 5))) { + fwrite($fs, $http_request); + while ( !feof($fs) ) { + $response .= fgets($fs, 1160); // One TCP-IP packet + } + fclose($fs); + list($headers, $body) = explode("\r\n\r\n", $response); + $headers = explode("\r\n", $headers); + $body = explode("\r\n", $body); + $response = new ArrayObject( + array("headers" => $headers, "body" => $body), ArrayObject::ARRAY_AS_PROPS); + } else { + throw new Exception("@todo CONNECTION TO URL SHORTENING SERVICE FAILED"); + } + Kohana_Log::add("debug", "Received response\n" . print_r($response, 1)); + + return $response; + } + + static function build_link($path='/') { + $protocol = strtolower(substr($_SERVER["SERVER_PROTOCOL"], 0, 5)) == 'https' ? 'https' : 'http'; + return url::site($path, $protocol); + } + + /** + * Shorten a Gallery URL + * @param string $long_url + * @param string $format + * @return string + */ + static function shorten_url($long_url, $format='json') { + $short_url = ''; + $parameters = array( + "login" => module::get_var("bitly", "login"), + 'apiKey' => module::get_var("bitly", "api_key"), + 'longUrl' => $long_url, + 'domain' => module::get_var("bitly", "domain"), + 'format' => $format, + ); + $request = self::_build_http_request('shorten', $parameters); + $response = self::_http_post($request, self::$api_host); + $json_decoded = json_decode($response->body[0]); + if ('OK' == $json_decoded->status_txt) { + // Save the original item id, the hash, and possibly the global hash, to the database + $short_url = $json_decoded->data->url; + $hash = $json_decoded->data->hash; + $global_hash = $json_decoded->data->global_hash; + $new_hash = $json_decoded->data->new_hash; + message::success("The $long_url has been shortened to $short_url"); + } else { + message::error("Unable to shorten the url"); + // @todo log the error + } + return $short_url; + } + + /** + * returns expanded url + * { + "status_code": 200, + "data": { + "expand": [ + { + "short_url": "http://tcrn.ch/a4MSUH", + "global_hash": "bWw49z", + "long_url": "http://www.techcrunch.com/2010/01/29/windows-mobile-foursquare/", + "user_hash": "a4MSUH" + }, + { + "short_url": "http://bit.ly/1YKMfY", + "global_hash": "1YKMfY", + "long_url": "http://betaworks.com/", + "user_hash": "1YKMfY" + }, + { + "long_url": "http://www.scotster.com/qf/?1152", + "global_hash": "lLWr", + "hash": "j3", + "user_hash": "j3" + }, + { + "hash": "a35.", + "error": "NOT_FOUND" + } + ] + }, + "status_txt": "OK" + } + */ + /** + * + * @param $short_url + * @param $hash + * @param $format + * @return + */ + static function expand_url($short_url, $hash=null, $format='json') { + $parameters = array( + "login" => module::get_var("bitly", "login"), + 'apiKey' => module::get_var("bitly", "api_key"), + 'shortUrl' => $short_url, + 'hash' => $hash, + ); + $request = self::_build_http_request('expand', $parameters); + $response = self::_http_post($http_request, self::$api_host); + return $response; + } + +} diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php new file mode 100644 index 00000000..49ea72a6 --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -0,0 +1,50 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("bitly_menu") + ->label(t("Bit.ly")) + ->url(url::site("admin/bitly"))); + } + + static function site_menu($menu, $theme) { + $item = $theme->item(); + if ($item && $item->type == "photo") { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten Link with bit.ly")) + ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) + ->css_id("g-bitly-link") + ->css_class("g-print-bitly-link ui-icon-print")); + } + } + + static function context_menu($menu, $theme, $item) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten Link with bit.ly")) + ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) + ->css_class("g-bitly-link ui-icon-link")); + } +} diff --git a/3.1/modules/bitly/helpers/bitly_installer.php b/3.1/modules/bitly/helpers/bitly_installer.php new file mode 100644 index 00000000..c847ec7c --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly_installer.php @@ -0,0 +1,38 @@ +query("CREATE TABLE {bitly_urls} ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `item_id` int(9) NOT NULL, + `hash` char(6) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("bitly", 1); + bitly::check_config(); + } + + static function deactivate() { + site_status::clear("bitly_config"); + } +} diff --git a/3.1/modules/bitly/helpers/bitly_theme.php b/3.1/modules/bitly/helpers/bitly_theme.php new file mode 100644 index 00000000..41aba38f --- /dev/null +++ b/3.1/modules/bitly/helpers/bitly_theme.php @@ -0,0 +1,24 @@ +script("bitly.js"); + } +} diff --git a/3.1/modules/bitly/js/bitly.js b/3.1/modules/bitly/js/bitly.js new file mode 100644 index 00000000..35bcd58b --- /dev/null +++ b/3.1/modules/bitly/js/bitly.js @@ -0,0 +1,8 @@ +$(document).ready(function() { + $(".g-bitly-link").click(function(e) { + e.preventDefault(); + return; + }); +}); + + diff --git a/3.1/modules/bitly/models/bitly.php b/3.1/modules/bitly/models/bitly.php new file mode 100644 index 00000000..a1b68600 --- /dev/null +++ b/3.1/modules/bitly/models/bitly.php @@ -0,0 +1,29 @@ + +
                      +

                      +

                      + bit.ly account which will provide an API Key, which is also free.", + array("api_key_url" => "http://bit.ly/a/your_api_key", + "bitly_url" => "http://bit.ly")) ?> +

                      +
                      + +
                      + %g3_url", array('g3_url' => $g3_url)) ?> +
                      + + + +
                      +
                      From c2bd8ef5baffb65a69c84780722c8a1573b6b480 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 25 Dec 2010 02:22:23 +0800 Subject: [PATCH 175/300] Changed table name and user_id is now owner_id, as it should be. --- 3.1/modules/bitly/helpers/bitly_installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_installer.php b/3.1/modules/bitly/helpers/bitly_installer.php index c847ec7c..c55fbb66 100644 --- a/3.1/modules/bitly/helpers/bitly_installer.php +++ b/3.1/modules/bitly/helpers/bitly_installer.php @@ -21,11 +21,11 @@ class bitly_installer { static function install() { Database::instance() - ->query("CREATE TABLE {bitly_urls} ( + ->query("CREATE TABLE {bitly_links} ( `id` int(9) NOT NULL AUTO_INCREMENT, `item_id` int(9) NOT NULL, `hash` char(6) NOT NULL, - `user_id` int(9) NOT NULL, + `owner_id` int(9) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); module::set_version("bitly", 1); From c521fede9f0471dcfdc047b0df282826eb5afa5b Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 25 Dec 2010 06:04:42 +0800 Subject: [PATCH 176/300] Store shortened bit.ly hashes locally. Implemented site menu's shorten link, context menu's not working yet. --- 3.1/modules/bitly/controllers/admin_bitly.php | 9 +- 3.1/modules/bitly/helpers/bitly.php | 96 +++++-------------- 3.1/modules/bitly/helpers/bitly_event.php | 20 ++-- 3.1/modules/bitly/models/bitly.php | 29 ------ 4 files changed, 42 insertions(+), 112 deletions(-) delete mode 100644 3.1/modules/bitly/models/bitly.php diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php index ee7e9f6f..f4240a63 100644 --- a/3.1/modules/bitly/controllers/admin_bitly.php +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -81,9 +81,14 @@ class Admin_Bitly_Controller extends Admin_Controller { $view->content->domain = $form->configure_bitly->domain->value; $view->content->valid_config = $valid_config; $view->content->form = $form; + if ($valid_config) { - // @todo Store/get G3's root bit.ly url, only shorten if it hasn't been stored. - $view->content->g3_url = bitly::shorten_url(bitly::build_link()); + $link = ORM::factory("bitly_link")->where("item_id", "=", 1)->find(); + if ($link->loaded()) { + $view->content->g3_url = "http://" . module::get_var("bitly", "domain") . "/$link->hash"; + } else { + $view->content->g3_url = bitly::shorten_url(1); + } } print $view; } diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index b69f7fe1..276c7796 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -165,19 +165,17 @@ class bitly_Core { return $response; } - static function build_link($path='/') { - $protocol = strtolower(substr($_SERVER["SERVER_PROTOCOL"], 0, 5)) == 'https' ? 'https' : 'http'; - return url::site($path, $protocol); - } - /** * Shorten a Gallery URL - * @param string $long_url + * @param int $item_id * @param string $format - * @return string + * @return mixed string|false */ - static function shorten_url($long_url, $format='json') { + static function shorten_url($item_id, $format='json') { + $item = ORM::factory("item", $item_id); $short_url = ''; + $long_url = url::abs_site($item->relative_url_cache); + $parameters = array( "login" => module::get_var("bitly", "login"), 'apiKey' => module::get_var("bitly", "api_key"), @@ -185,73 +183,31 @@ class bitly_Core { 'domain' => module::get_var("bitly", "domain"), 'format' => $format, ); + $request = self::_build_http_request('shorten', $parameters); $response = self::_http_post($request, self::$api_host); - $json_decoded = json_decode($response->body[0]); - if ('OK' == $json_decoded->status_txt) { - // Save the original item id, the hash, and possibly the global hash, to the database - $short_url = $json_decoded->data->url; - $hash = $json_decoded->data->hash; - $global_hash = $json_decoded->data->global_hash; - $new_hash = $json_decoded->data->new_hash; - message::success("The $long_url has been shortened to $short_url"); + $json_response = json_decode($response->body[0]); + + if ('OK' == $json_response->status_txt) { + $short_url = $json_response->data->url; + // Save the link hash to the database + $link = ORM::factory("bitly_link"); + $link->item_id = $item_id; + $link->hash = $json_response->data->hash; + //$link->global_hash = $json_response->data->global_hash; + //$new_hash = $json_response->data->new_hash; + $link->owner_id = $item->owner_id; + $link->save(); + + message::success("$long_url has been shortened to $short_url"); + + return $json_response->data->url; + } else { - message::error("Unable to shorten the url"); + message::error("Unable to shorten $long_url"); // @todo log the error + return false; } - return $short_url; } - /** - * returns expanded url - * { - "status_code": 200, - "data": { - "expand": [ - { - "short_url": "http://tcrn.ch/a4MSUH", - "global_hash": "bWw49z", - "long_url": "http://www.techcrunch.com/2010/01/29/windows-mobile-foursquare/", - "user_hash": "a4MSUH" - }, - { - "short_url": "http://bit.ly/1YKMfY", - "global_hash": "1YKMfY", - "long_url": "http://betaworks.com/", - "user_hash": "1YKMfY" - }, - { - "long_url": "http://www.scotster.com/qf/?1152", - "global_hash": "lLWr", - "hash": "j3", - "user_hash": "j3" - }, - { - "hash": "a35.", - "error": "NOT_FOUND" - } - ] - }, - "status_txt": "OK" - } - */ - /** - * - * @param $short_url - * @param $hash - * @param $format - * @return - */ - static function expand_url($short_url, $hash=null, $format='json') { - $parameters = array( - "login" => module::get_var("bitly", "login"), - 'apiKey' => module::get_var("bitly", "api_key"), - 'shortUrl' => $short_url, - 'hash' => $hash, - ); - $request = self::_build_http_request('expand', $parameters); - $response = self::_http_post($http_request, self::$api_host); - return $response; - } - } diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 49ea72a6..3701df8e 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -28,15 +28,13 @@ class bitly_event_Core { static function site_menu($menu, $theme) { $item = $theme->item(); - if ($item && $item->type == "photo") { - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten Link with bit.ly")) - ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) - ->css_id("g-bitly-link") - ->css_class("g-print-bitly-link ui-icon-print")); - } + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten Link with bit.ly")) + ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->css_id("g-bitly-link") + ->css_class("g-bitly-shorten ui-icon-link")); } static function context_menu($menu, $theme, $item) { @@ -44,7 +42,7 @@ class bitly_event_Core { ->append(Menu::factory("link") ->id("bitly") ->label(t("Shorten Link with bit.ly")) - ->url(url::site("bitly/shorten_link/$item->id?csrf=$theme->csrf")) - ->css_class("g-bitly-link ui-icon-link")); + ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->css_class("g-bitly-shorten ui-icon-link")); } } diff --git a/3.1/modules/bitly/models/bitly.php b/3.1/modules/bitly/models/bitly.php deleted file mode 100644 index a1b68600..00000000 --- a/3.1/modules/bitly/models/bitly.php +++ /dev/null @@ -1,29 +0,0 @@ - Date: Sun, 26 Dec 2010 06:24:15 +0800 Subject: [PATCH 177/300] Store shortened bit.ly hashes locally. Implemented site menu's shorten link, context menu's not working yet. (missed adding the controller in original commit) --- 3.1/modules/bitly/controllers/bitly.php | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 3.1/modules/bitly/controllers/bitly.php diff --git a/3.1/modules/bitly/controllers/bitly.php b/3.1/modules/bitly/controllers/bitly.php new file mode 100644 index 00000000..608918d3 --- /dev/null +++ b/3.1/modules/bitly/controllers/bitly.php @@ -0,0 +1,43 @@ +relative_url_cache)); + } + +} \ No newline at end of file From 3f96ef94db39ac1f177800a7ed5867a17ab86b38 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 26 Dec 2010 06:37:32 +0800 Subject: [PATCH 178/300] Hooked up shorten link for contextual menu. --- 3.1/modules/bitly/js/bitly.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/3.1/modules/bitly/js/bitly.js b/3.1/modules/bitly/js/bitly.js index 35bcd58b..96dcb427 100644 --- a/3.1/modules/bitly/js/bitly.js +++ b/3.1/modules/bitly/js/bitly.js @@ -1,8 +1,6 @@ $(document).ready(function() { - $(".g-bitly-link").click(function(e) { + $(".g-bitly-shorten").click(function(e) { e.preventDefault(); - return; + return window.location = ($(this).attr("href")); }); }); - - From a63742ac9d7bcc3ea01e02de583b7bce0a0773ce Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 26 Dec 2010 11:19:46 -0700 Subject: [PATCH 179/300] Update link labels to adhere to capitalization rules. --- 3.1/modules/bitly/helpers/bitly_event.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 3701df8e..4074b4e4 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -31,7 +31,7 @@ class bitly_event_Core { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t("Shorten Link with bit.ly")) + ->label(t("Shorten link with bit.ly")) ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) ->css_id("g-bitly-link") ->css_class("g-bitly-shorten ui-icon-link")); @@ -41,7 +41,7 @@ class bitly_event_Core { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t("Shorten Link with bit.ly")) + ->label(t("Shorten link with bit.ly")) ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) ->css_class("g-bitly-shorten ui-icon-link")); } From d422f581a47093234a20af96b145d9c243c72b20 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 26 Dec 2010 16:18:07 -0700 Subject: [PATCH 180/300] Dropped owner_id, it can be derived via the item. Added global_hash --- 3.1/modules/bitly/helpers/bitly.php | 4 +--- 3.1/modules/bitly/helpers/bitly_installer.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 276c7796..6462804b 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -194,9 +194,7 @@ class bitly_Core { $link = ORM::factory("bitly_link"); $link->item_id = $item_id; $link->hash = $json_response->data->hash; - //$link->global_hash = $json_response->data->global_hash; - //$new_hash = $json_response->data->new_hash; - $link->owner_id = $item->owner_id; + $link->global_hash = $json_response->data->global_hash; $link->save(); message::success("$long_url has been shortened to $short_url"); diff --git a/3.1/modules/bitly/helpers/bitly_installer.php b/3.1/modules/bitly/helpers/bitly_installer.php index c55fbb66..4fd0c8ad 100644 --- a/3.1/modules/bitly/helpers/bitly_installer.php +++ b/3.1/modules/bitly/helpers/bitly_installer.php @@ -25,7 +25,7 @@ class bitly_installer { `id` int(9) NOT NULL AUTO_INCREMENT, `item_id` int(9) NOT NULL, `hash` char(6) NOT NULL, - `owner_id` int(9) NOT NULL, + `global_hash` char(6) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); module::set_version("bitly", 1); From d0cfcbf20edd9f84f758ee59bb129e42c380154d Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 26 Dec 2010 18:03:08 -0700 Subject: [PATCH 181/300] Add check to see if current user is the owner before displaying shorten link menu item. --- 3.1/modules/bitly/helpers/bitly_event.php | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 4074b4e4..29463606 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -27,22 +27,25 @@ class bitly_event_Core { } static function site_menu($menu, $theme) { - $item = $theme->item(); - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten link with bit.ly")) - ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) - ->css_id("g-bitly-link") - ->css_class("g-bitly-shorten ui-icon-link")); + if ($theme->item->owner->id == identity::active_user()->id) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten link with bit.ly")) + ->url(url::site("bitly/shorten/{$theme->item->id}?csrf=$theme->csrf")) + ->css_id("g-bitly-link") + ->css_class("g-bitly-shorten ui-icon-link")); + } } static function context_menu($menu, $theme, $item) { - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten link with bit.ly")) - ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) - ->css_class("g-bitly-shorten ui-icon-link")); + if ($theme->item->owner->id == identity::active_user()->id) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten link with bit.ly")) + ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->css_class("g-bitly-shorten ui-icon-link")); + } } } From e1ad88ab10526e6023566152c46bf3057924de6a Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 26 Dec 2010 22:58:06 -0700 Subject: [PATCH 182/300] Removed debug line. --- 3.1/modules/bitly/helpers/bitly.php | 1 - 1 file changed, 1 deletion(-) diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 6462804b..2ba90670 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -145,7 +145,6 @@ class bitly_Core { */ private static function _http_post($http_request) { $response = ''; - //Kohana_Log::add("debug", "Send request\n" . print_r($http_request, 1)); if (false !== ($fs = @fsockopen(self::$api_host, 80, $errno, $errstr, 5))) { fwrite($fs, $http_request); while ( !feof($fs) ) { From a6bc9ab2dbd9a37ccd8297296869d7325895c392 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 27 Dec 2010 02:19:46 +0800 Subject: [PATCH 183/300] Update link labels to adhere to capitalization rules. --- 3.1/modules/bitly/helpers/bitly_event.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 3701df8e..4074b4e4 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -31,7 +31,7 @@ class bitly_event_Core { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t("Shorten Link with bit.ly")) + ->label(t("Shorten link with bit.ly")) ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) ->css_id("g-bitly-link") ->css_class("g-bitly-shorten ui-icon-link")); @@ -41,7 +41,7 @@ class bitly_event_Core { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t("Shorten Link with bit.ly")) + ->label(t("Shorten link with bit.ly")) ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) ->css_class("g-bitly-shorten ui-icon-link")); } From 38f1d0c4b723e15fdc7095b90a70b2c3a28a143d Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 27 Dec 2010 07:18:07 +0800 Subject: [PATCH 184/300] Dropped owner_id, it can be derived via the item. Added global_hash --- 3.1/modules/bitly/helpers/bitly.php | 4 +--- 3.1/modules/bitly/helpers/bitly_installer.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 276c7796..6462804b 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -194,9 +194,7 @@ class bitly_Core { $link = ORM::factory("bitly_link"); $link->item_id = $item_id; $link->hash = $json_response->data->hash; - //$link->global_hash = $json_response->data->global_hash; - //$new_hash = $json_response->data->new_hash; - $link->owner_id = $item->owner_id; + $link->global_hash = $json_response->data->global_hash; $link->save(); message::success("$long_url has been shortened to $short_url"); diff --git a/3.1/modules/bitly/helpers/bitly_installer.php b/3.1/modules/bitly/helpers/bitly_installer.php index c55fbb66..4fd0c8ad 100644 --- a/3.1/modules/bitly/helpers/bitly_installer.php +++ b/3.1/modules/bitly/helpers/bitly_installer.php @@ -25,7 +25,7 @@ class bitly_installer { `id` int(9) NOT NULL AUTO_INCREMENT, `item_id` int(9) NOT NULL, `hash` char(6) NOT NULL, - `owner_id` int(9) NOT NULL, + `global_hash` char(6) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); module::set_version("bitly", 1); From 4c047fee13514a337a4f6c20e81aa2e8ca693b1c Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 27 Dec 2010 09:03:08 +0800 Subject: [PATCH 185/300] Add check to see if current user is the owner before displaying shorten link menu item. --- 3.1/modules/bitly/helpers/bitly_event.php | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 4074b4e4..29463606 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -27,22 +27,25 @@ class bitly_event_Core { } static function site_menu($menu, $theme) { - $item = $theme->item(); - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten link with bit.ly")) - ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) - ->css_id("g-bitly-link") - ->css_class("g-bitly-shorten ui-icon-link")); + if ($theme->item->owner->id == identity::active_user()->id) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten link with bit.ly")) + ->url(url::site("bitly/shorten/{$theme->item->id}?csrf=$theme->csrf")) + ->css_id("g-bitly-link") + ->css_class("g-bitly-shorten ui-icon-link")); + } } static function context_menu($menu, $theme, $item) { - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten link with bit.ly")) - ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) - ->css_class("g-bitly-shorten ui-icon-link")); + if ($theme->item->owner->id == identity::active_user()->id) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t("Shorten link with bit.ly")) + ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->css_class("g-bitly-shorten ui-icon-link")); + } } } From 85025d16d3f2041d90cfd870c5e816aa22e965a4 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 27 Dec 2010 13:58:06 +0800 Subject: [PATCH 186/300] Removed debug line. --- 3.1/modules/bitly/helpers/bitly.php | 1 - 1 file changed, 1 deletion(-) diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 6462804b..2ba90670 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -145,7 +145,6 @@ class bitly_Core { */ private static function _http_post($http_request) { $response = ''; - //Kohana_Log::add("debug", "Send request\n" . print_r($http_request, 1)); if (false !== ($fs = @fsockopen(self::$api_host, 80, $errno, $errstr, 5))) { fwrite($fs, $http_request); while ( !feof($fs) ) { From dbf31cd3207b7a2b1b9617d75224a77409abcee7 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Tue, 28 Dec 2010 21:15:09 -0700 Subject: [PATCH 187/300] Don't show shorten link if the link's already short! Only show the shorten link to the item's owner. --- 3.1/modules/bitly/controllers/admin_bitly.php | 2 +- 3.1/modules/bitly/helpers/bitly.php | 12 +++++++ 3.1/modules/bitly/helpers/bitly_event.php | 33 +++++++++++-------- 3 files changed, 32 insertions(+), 15 deletions(-) diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php index f4240a63..8576c9d7 100644 --- a/3.1/modules/bitly/controllers/admin_bitly.php +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -85,7 +85,7 @@ class Admin_Bitly_Controller extends Admin_Controller { if ($valid_config) { $link = ORM::factory("bitly_link")->where("item_id", "=", 1)->find(); if ($link->loaded()) { - $view->content->g3_url = "http://" . module::get_var("bitly", "domain") . "/$link->hash"; + $view->content->g3_url = bitly::bitly_link($link->hash); } else { $view->content->g3_url = bitly::shorten_url(1); } diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 2ba90670..f89093e8 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class bitly_Core { + public static $test_mode = TEST_MODE; public static $api_host = "api.bit.ly"; @@ -207,4 +208,15 @@ class bitly_Core { } } + /** + * Build a bit.ly link for a specified hash + * @param string $hash + * @return string + */ + static function bitly_link($hash) { + if (!empty($hash)) { + return "http://" . module::get_var("bitly", "domain") . "/$hash"; + } + } + } diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 29463606..86c970f3 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -18,6 +18,9 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class bitly_event_Core { + + public static $shorten_link_text = "Shorten link with bit.ly"; + static function admin_menu($menu, $theme) { $menu->get("settings_menu") ->append(Menu::factory("link") @@ -27,23 +30,25 @@ class bitly_event_Core { } static function site_menu($menu, $theme) { - if ($theme->item->owner->id == identity::active_user()->id) { - $menu->get("options_menu") - ->append(Menu::factory("link") - ->id("bitly") - ->label(t("Shorten link with bit.ly")) - ->url(url::site("bitly/shorten/{$theme->item->id}?csrf=$theme->csrf")) - ->css_id("g-bitly-link") - ->css_class("g-bitly-shorten ui-icon-link")); - } - } - - static function context_menu($menu, $theme, $item) { - if ($theme->item->owner->id == identity::active_user()->id) { + $link = ORM::factory("bitly_link")->where("item_id", "=", $theme->item->id)->find(); + if (!$link->loaded() && $theme->item->owner->id == identity::active_user()->id) { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t("Shorten link with bit.ly")) + ->label(t(self::$shorten_link_text)) + ->url(url::site("bitly/shorten/{$theme->item->id}?csrf=$theme->csrf")) + ->css_id("g-bitly-shorten") + ->css_class("g-bitly-shorten ui-icon-link")); + } + } + + static function context_menu($menu, $theme, $item) { + $link = ORM::factory("bitly_link")->where("item_id", "=", $item->id)->find(); + if (!$link->loaded() && $theme->item->owner->id == identity::active_user()->id) { + $menu->get("options_menu") + ->append(Menu::factory("link") + ->id("bitly") + ->label(t(self::$shorten_link_text)) ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) ->css_class("g-bitly-shorten ui-icon-link")); } From 7077fd9d0b47326af0aebb7f3a739cbfa6b9d1f9 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 29 Dec 2010 21:37:22 -0700 Subject: [PATCH 188/300] The settings link label should be bit.ly instead of Bit.ly. --- 3.1/modules/bitly/helpers/bitly_event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 86c970f3..899357ce 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -25,7 +25,7 @@ class bitly_event_Core { $menu->get("settings_menu") ->append(Menu::factory("link") ->id("bitly_menu") - ->label(t("Bit.ly")) + ->label(t("bit.ly")) ->url(url::site("admin/bitly"))); } From 354ed39dcde9e9ea1ce5784ff3fd8d9cb13136c7 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 29 Dec 2010 22:28:24 -0700 Subject: [PATCH 189/300] Don't attempt to shorten root album's URL if the module's not been configured yet. --- 3.1/modules/bitly/controllers/admin_bitly.php | 43 +++++++++---------- 3.1/modules/bitly/views/admin_bitly.html.php | 2 +- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php index 8576c9d7..4f82f913 100644 --- a/3.1/modules/bitly/controllers/admin_bitly.php +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -25,14 +25,14 @@ class Admin_Bitly_Controller extends Admin_Controller { */ public function index() { $form = bitly::get_configure_form(); - $valid_config = true; - + $login = module::get_var("bitly", "login"); + $api_key = module::get_var("bitly", "api_key"); + $domain = module::get_var("bitly", "domain"); + $valid_config = false; + if (request::method() == "post") { access::verify_csrf(); if ($form->validate()) { - $current_login = module::get_var("bitly", "login"); - $current_key = module::get_var("bitly", "api_key"); - $current_domain = module::get_var("bitly", "domain"); $new_login = $form->configure_bitly->login->value; $new_key = $form->configure_bitly->api_key->value; $new_domain = $form->configure_bitly->domain->value; @@ -44,24 +44,24 @@ class Admin_Bitly_Controller extends Admin_Controller { if (!bitly::check_config()) { url::redirect("admin/bitly"); } else { - if ($current_login && !$new_login) { + if ($login && !$new_login) { message::success(t("Your bit.ly login has been cleared.")); - } else if ($current_login && $new_login && $current_login != $new_login) { + } else if ($login && $new_login && $login != $new_login) { message::success(t("Your bit.ly login has been changed.")); - } else if (!$current_login && $new_login) { + } else if (!$login && $new_login) { message::success(t("Your bit.ly login has been saved.")); } - if ($current_key && !$new_key) { + if ($api_key && !$new_key) { message::success(t("Your bit.ly API key has been cleared.")); - } else if ($current_key && $new_key && $current_key != $new_key) { + } else if ($api_key && $new_key && $api_key != $new_key) { message::success(t("Your bit.ly API key has been changed.")); - } else if (!$current_key && $new_key) { + } else if (!$api_key && $new_key) { message::success(t("Your bit.ly API key has been saved.")); } - if ($current_domain && $new_domain && $current_domain != $new_domain) { + if ($domain && $new_domain && $domain != $new_domain) { message::success(t("Your preferrend bit.ly domain has been changed.")); - } else if (!$current_domain && $new_domain) { - message::success(t("Your preferred bit.ly domain has been set.")); + } else if (!$domain && $new_domain) { + message::success(t("Your preferred bit.ly domain has been saved.")); } log::success("bitly", t("bit.ly login changed to %new_login", array("new_login" => $new_login))); @@ -79,17 +79,16 @@ class Admin_Bitly_Controller extends Admin_Controller { $view->content->login = $form->configure_bitly->login->value; $view->content->api_key = $form->configure_bitly->api_key->value; $view->content->domain = $form->configure_bitly->domain->value; - $view->content->valid_config = $valid_config; $view->content->form = $form; - if ($valid_config) { - $link = ORM::factory("bitly_link")->where("item_id", "=", 1)->find(); - if ($link->loaded()) { - $view->content->g3_url = bitly::bitly_link($link->hash); - } else { - $view->content->g3_url = bitly::shorten_url(1); - } + $link = ORM::factory("bitly_link")->where("item_id", "=", 1)->find(); + + if ($link->loaded()) { + $view->content->g3_url = bitly::bitly_link($link->hash); + } else if ($valid_config && !empty($login) && !empty($api_key) && !empty($domain)) { + $view->content->g3_url = bitly::shorten_url(1); } + print $view; } diff --git a/3.1/modules/bitly/views/admin_bitly.html.php b/3.1/modules/bitly/views/admin_bitly.html.php index da84df54..d1d6703a 100644 --- a/3.1/modules/bitly/views/admin_bitly.html.php +++ b/3.1/modules/bitly/views/admin_bitly.html.php @@ -7,7 +7,7 @@ "bitly_url" => "http://bit.ly")) ?>

                      - +
                      %g3_url", array('g3_url' => $g3_url)) ?>
                      From 213ddf49fa63040cf4557cf749f94eb84d17ab2f Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Thu, 30 Dec 2010 14:35:24 -0700 Subject: [PATCH 190/300] Not sure how I missed checking in the model stub, but here it is. --- 3.1/modules/bitly/models/bitly_link.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 3.1/modules/bitly/models/bitly_link.php diff --git a/3.1/modules/bitly/models/bitly_link.php b/3.1/modules/bitly/models/bitly_link.php new file mode 100644 index 00000000..b5c967a6 --- /dev/null +++ b/3.1/modules/bitly/models/bitly_link.php @@ -0,0 +1,21 @@ + Date: Thu, 30 Dec 2010 14:48:19 -0700 Subject: [PATCH 191/300] Minor updates to address Bharat's review comments. Thanks Bharat! --- 3.1/modules/bitly/controllers/admin_bitly.php | 2 +- 3.1/modules/bitly/helpers/bitly.php | 2 +- 3.1/modules/bitly/helpers/bitly_event.php | 10 ++++------ 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/3.1/modules/bitly/controllers/admin_bitly.php b/3.1/modules/bitly/controllers/admin_bitly.php index 4f82f913..8ebaaff4 100644 --- a/3.1/modules/bitly/controllers/admin_bitly.php +++ b/3.1/modules/bitly/controllers/admin_bitly.php @@ -84,7 +84,7 @@ class Admin_Bitly_Controller extends Admin_Controller { $link = ORM::factory("bitly_link")->where("item_id", "=", 1)->find(); if ($link->loaded()) { - $view->content->g3_url = bitly::bitly_link($link->hash); + $view->content->g3_url = bitly::url($link->hash); } else if ($valid_config && !empty($login) && !empty($api_key) && !empty($domain)) { $view->content->g3_url = bitly::shorten_url(1); } diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index f89093e8..5f99d87c 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -213,7 +213,7 @@ class bitly_Core { * @param string $hash * @return string */ - static function bitly_link($hash) { + static function url($hash) { if (!empty($hash)) { return "http://" . module::get_var("bitly", "domain") . "/$hash"; } diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 899357ce..9a55fe95 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -19,8 +19,6 @@ */ class bitly_event_Core { - public static $shorten_link_text = "Shorten link with bit.ly"; - static function admin_menu($menu, $theme) { $menu->get("settings_menu") ->append(Menu::factory("link") @@ -35,8 +33,8 @@ class bitly_event_Core { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t(self::$shorten_link_text)) - ->url(url::site("bitly/shorten/{$theme->item->id}?csrf=$theme->csrf")) + ->label(t("Shorten link with bit.ly")) + ->url(url::site("bitly/shorten/{$theme->item->id}?csrf={$theme->csrf}")) ->css_id("g-bitly-shorten") ->css_class("g-bitly-shorten ui-icon-link")); } @@ -48,8 +46,8 @@ class bitly_event_Core { $menu->get("options_menu") ->append(Menu::factory("link") ->id("bitly") - ->label(t(self::$shorten_link_text)) - ->url(url::site("bitly/shorten/$item->id?csrf=$theme->csrf")) + ->label(t("Shorten link with bit.ly")) + ->url(url::site("bitly/shorten/{$item->id}?csrf={$theme->csrf}")) ->css_class("g-bitly-shorten ui-icon-link")); } } From 00cc49d7feda683c030467deedc50789736bc2a8 Mon Sep 17 00:00:00 2001 From: colings Date: Sat, 1 Jan 2011 11:26:39 -0600 Subject: [PATCH 192/300] Initial add of Picasa Faces module --- .../helpers/picasa_faces_event.php | 22 ++ .../helpers/picasa_faces_installer.php | 50 +++ .../helpers/picasa_faces_task.php | 312 +++++++++++++++++ .../helpers/picasa_faces_task.php.bak | 313 ++++++++++++++++++ .../picasa_faces/models/picasa_face.php | 5 + 3.0/modules/picasa_faces/module.info | 3 + 6 files changed, 705 insertions(+) create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_event.php create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_installer.php create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_task.php create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak create mode 100644 3.0/modules/picasa_faces/models/picasa_face.php create mode 100644 3.0/modules/picasa_faces/module.info diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_event.php b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php new file mode 100644 index 00000000..29886370 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php @@ -0,0 +1,22 @@ +deactivate)) + { + site_status::warning( + t("The Picasa Faces module requires the Photo Annotation module. " . + "Activate the Photo Annotation module now", + array("url" => url::site("admin/modules"))), + "picasa_faces_needs_photoannotation"); + } + else + { + site_status::clear("picasa_faces_needs_photoannotation"); + } + } +} diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php new file mode 100644 index 00000000..571626f3 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php @@ -0,0 +1,50 @@ +query( + "CREATE TABLE IF NOT EXISTS `picasa_faces` ( + `id` int(9) NOT NULL auto_increment, + `face_id` varchar(16) NOT NULL, + `tag_id` int(9) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY `face_id` (`face_id`,`id`) + ) DEFAULT CHARSET=utf8;" + ); + + // Set the module version number. + module::set_version("picasa_faces", 2); + } + + static function upgrade($version) + { + if ($version == 1) + { + Database::instance()->query( + "ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL" + ); + + module::set_version("picasa_faces", 2); + } + } + + static function deactivate() + { + // Clear the require photo annototaion message when picasa faces is deactivated. + site_status::clear("picasa_faces_needs_photoannotation"); + } + + static function uninstall() + { + // Delete the face mapping table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {picasa_faces};"); + module::delete("picasa_faces"); + } +} diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php new file mode 100644 index 00000000..e845b593 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php @@ -0,0 +1,312 @@ +callback("picasa_faces_task::import_faces") + ->name(t("Import faces from Picasa")) + ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) + ->severity(log::SUCCESS)); + } + + static function import_faces($task) + { + if (!module::is_active("photoannotation")) + { + $task->done = true; + $task->status = t("Photo Annotation module is inactive, no faces will be imported"); + return; + } + + $start = microtime(true); + + // Figure out the total number of albums in the database. + // If this is the first run, also set last_id and completed to 0. + $total = $task->get("total"); + if (empty($total)) + { + $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); + $task->set("last_id", 0); + $task->set("completed", 0); + $task->set("new_faces", 0); + $task->set("old_faces", 0); + } + $last_id = $task->get("last_id"); + $completed = $task->get("completed"); + $new_faces = $task->get("new_faces"); + $old_faces = $task->get("old_faces"); + + // Try to find a contacts.xml file, and parse out the contents if it exists + $contacts = null; + $contactsXML = VARPATH . "albums/contacts.xml"; + if (file_exists($contactsXML)) + { + $xml = simplexml_load_file($contactsXML); + $contacts = $xml->contact; + } + + // Check each album in the database to see if it has a .picasa.ini file on disk, + // and extract any faces if it does. + foreach (ORM::factory("item") + ->where("id", ">", $last_id) + ->where("type", "=", "album") + ->find_all(100) as $albumItem) + { + $picasaFile = $albumItem->file_path()."/.picasa.ini"; + if (file_exists($picasaFile)) + { + // Parse the .picasa.ini file and extract any faces + $photosWithFaces = self::parsePicasaIni($picasaFile); + + // Build a mapping from photo filenames in this album to the items + $photos = array(); + foreach ($albumItem->children() as $child) + { + if ($child->is_photo()) + { + $photos[$child->name] = $child; + } + } + + // Iterate through all the photos with faces in them + foreach ($photosWithFaces as $photoName => $faces) + { + // Find the item for this photo + $photoItem = $photos[$photoName]; + if ($photoItem) + { + foreach ($faces as $faceId => $faceCoords) + { + $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); + + // If we don't already have a mapping for this face, create one + if (!$faceMapping->loaded()) + { + $newTagId = self::getFaceMapping($faceId, $contacts); + + // Save the mapping from Picasa face id to tag id, so + // faces will be grouped properly + $faceMapping->face_id = $faceId; + $faceMapping->tag_id = $newTagId; + $faceMapping->user_id = 0; + $faceMapping->save(); + } + + if ($faceMapping->user_id == 0) + { + $numTagsOnPhoto = ORM::factory("items_face") + ->where("tag_id", "=", $faceMapping->tag_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + else + { + $numTagsOnPhoto = ORM::factory("items_user") + ->where("user_id", "=", $faceMapping->user_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + + // If this face tag isn't already on this photo, add it (we + // assume a face should only ever appear once per photo) + if ($numTagsOnPhoto == 0) + { + self::addNewFace($faceMapping, $faceCoords, $photoItem); + $new_faces++; + } + else + { + $old_faces++; + } + } + } + } + } + + $last_id = $albumItem->id; + $completed++; + + if ($completed == $total || microtime(true) - $start > 1.5) + { + break; + } + } + + $task->set("completed", $completed); + $task->set("last_id", $last_id); + $task->set("new_faces", $new_faces); + $task->set("old_faces", $old_faces); + + if ($total == $completed) + { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + } + else + { + $task->percent_complete = round(100 * $completed / $total); + } + + $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", + array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + } + + static function getFaceMapping($faceId, $contacts) + { + $personTag = null; + + // If we have a contacts.xml file, try to find the face id there + if ($contacts != null) + { + foreach ($contacts as $contact) + { + if ($contact['id'] == $faceId) + { + // We found this face id in the contacts.xml. See if a tag already exists. + $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); + + // If the tag doesn't exist already, add it + if (!$personTag->loaded()) + { + $personTag->name = $contact['name']; + $personTag->save(); + } + + break; + } + } + } + + // We either didn't find the face in contacts.xml, or we don't have contacts.xml. + // Add the face using a generic name. + if ($personTag == null) + { + // Find an unused "Picasa x" tag + $personID = 0; + $personName = ""; + do + { + $personID++; + $personName = "Picasa ".$personID; + $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); + } while ($personTag->loaded()); + + // We found an open name, save it so we can get the id + $personTag->name = $personName; + $personTag->save(); + } + + return $personTag->id; + } + + static function addNewFace($faceMapping, $faceCoords, $photoItem) + { + // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap + // that to the resize dimensions. + $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); + $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); + $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); + $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); + + if ($faceMapping->user_id == 0) + { + // Add the photo to this tag + $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); + $tag->add($photoItem); + $tag->count++; + $tag->save(); + + // Add the face + $newFace = ORM::factory("items_face"); + $newFace->tag_id = $faceMapping->tag_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + else + { + // Add the face + $newFace = ORM::factory("items_user"); + $newFace->user_id = $faceMapping->user_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + } + + static function parsePicasaIni($filePath) + { + // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it + $ini_lines = file($filePath); + + $curFilename = ""; + + $photosWithFaces = array(); + + foreach ($ini_lines as $ini_line) + { + // Trim off any whitespace at the ends + $ini_line = trim($ini_line); + + if ($ini_line[0] == '[') + { + // If this line starts with [ it's a filename, strip off the brackets + $curFilename = substr($ini_line, 1, -1); + } + else + { + // If this isn't a filename, it must be data for a file, get the key/value pair + $photoData = explode("=", $ini_line); + + if ($photoData[0] == "faces") + { + // If it's face data, break it up by face + $faces = explode(";", $photoData[1]); + + $photoFaces = array(); + + foreach ($faces as $face) + { + // Split a face into the rectangle and face id + $splitface = explode(",", $face); + + $hexrect = substr($splitface[0], 7, -1); + // We need a string with 16 chars. Fill up with zeros from left. + $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); + $person = $splitface[1]; + + // The rectangle is 4 4-character hex values + $left = hexdec(substr($hexrect,0,4)); + $top = hexdec(substr($hexrect,4,4)); + $right = hexdec(substr($hexrect,8,4)); + $bottom = hexdec(substr($hexrect,12,4)); + + $facePos = array("left" => $left, + "top" => $top, + "right" => $right, + "bottom" => $bottom); + + $photoFaces[$person] = $facePos; + } + + $photosWithFaces[$curFilename] = $photoFaces; + } + } + } + + return $photosWithFaces; + } +} + +?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak new file mode 100644 index 00000000..7bdff7a7 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak @@ -0,0 +1,313 @@ +callback("picasa_faces_task::import_faces") + ->name(t("Import faces from Picasa")) + ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) + ->severity(log::SUCCESS)); + } + + static function import_faces($task) + { + if (!module::is_active("photoannotation")) + { + $task->done = true; + $task->status = t("Photo Annotation module is inactive, no faces will be imported"); + return; + } + + $start = microtime(true); + + // Figure out the total number of albums in the database. + // If this is the first run, also set last_id and completed to 0. + $total = $task->get("total"); + if (empty($total)) + { + $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); + $task->set("last_id", 0); + $task->set("completed", 0); + $task->set("new_faces", 0); + $task->set("old_faces", 0); + } + + $last_id = $task->get("last_id"); + $completed = $task->get("completed"); + $new_faces = $task->get("new_faces"); + $old_faces = $task->get("old_faces"); + + // Try to find a contacts.xml file, and parse out the contents if it exists + $contacts = null; + $contactsXML = VARPATH . "albums/contacts.xml"; + if (file_exists($contactsXML)) + { + $xml = simplexml_load_file($contactsXML); + $contacts = $xml->contact; + } + + // Check each album in the database to see if it has a .picasa.ini file on disk, + // and extract any faces if it does. + foreach (ORM::factory("item") + ->where("id", ">", $last_id) + ->where("type", "=", "album") + ->find_all(100) as $albumItem) + { + $picasaFile = $albumItem->file_path()."/.picasa.ini"; + if (file_exists($picasaFile)) + { + // Parse the .picasa.ini file and extract any faces + $photosWithFaces = self::parsePicasaIni($picasaFile); + + // Build a mapping from photo filenames in this album to the items + $photos = array(); + foreach ($albumItem->children() as $child) + { + if ($child->is_photo()) + { + $photos[$child->name] = $child; + } + } + + // Iterate through all the photos with faces in them + foreach ($photosWithFaces as $photoName => $faces) + { + // Find the item for this photo + $photoItem = $photos[$photoName]; + if ($photoItem) + { + foreach ($faces as $faceId => $faceCoords) + { + $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); + + // If we don't already have a mapping for this face, create one + if (!$faceMapping->loaded()) + { + $newTagId = self::getFaceMapping($faceId, $contacts); + + // Save the mapping from Picasa face id to tag id, so + // faces will be grouped properly + $faceMapping->face_id = $faceId; + $faceMapping->tag_id = $newTagId; + $faceMapping->user_id = 0; + $faceMapping->save(); + } + + if ($faceMapping->user_id == 0) + { + $numTagsOnPhoto = ORM::factory("items_face") + ->where("tag_id", "=", $faceMapping->tag_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + else + { + $numTagsOnPhoto = ORM::factory("items_user") + ->where("user_id", "=", $faceMapping->user_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + + // If this face tag isn't already on this photo, add it (we + // assume a face should only ever appear once per photo) + if ($numTagsOnPhoto == 0) + { + self::addNewFace($faceMapping, $faceCoords, $photoItem); + $new_faces++; + } + else + { + $old_faces++; + } + } + } + } + } + + $last_id = $albumItem->id; + $completed++; + + if ($completed == $total || microtime(true) - $start > 1.5) + { + break; + } + } + + $task->set("completed", $completed); + $task->set("last_id", $last_id); + $task->set("new_faces", $new_faces); + $task->set("old_faces", $old_faces); + + if ($total == $completed) + { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + } + else + { + $task->percent_complete = round(100 * $completed / $total); + } + + $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", + array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + } + + static function getFaceMapping($faceId, $contacts) + { + $personTag = null; + + // If we have a contacts.xml file, try to find the face id there + if ($contacts != null) + { + foreach ($contacts as $contact) + { + if ($contact['id'] == $faceId) + { + // We found this face id in the contacts.xml. See if a tag already exists. + $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); + + // If the tag doesn't exist already, add it + if (!$personTag->loaded()) + { + $personTag->name = $contact['name']; + $personTag->save(); + } + + break; + } + } + } + + // We either didn't find the face in contacts.xml, or we don't have contacts.xml. + // Add the face using a generic name. + if ($personTag == null) + { + // Find an unused "Picasa x" tag + $personID = 0; + $personName = ""; + do + { + $personID++; + $personName = "Picasa ".$personID; + $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); + } while ($personTag->loaded()); + + // We found an open name, save it so we can get the id + $personTag->name = $personName; + $personTag->save(); + } + + return $personTag->id; + } + + static function addNewFace($faceMapping, $faceCoords, $photoItem) + { + // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap + // that to the resize dimensions. + $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); + $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); + $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); + $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); + + if ($faceMapping->user_id == 0) + { + // Add the photo to this tag + $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); + $tag->add($photoItem); + $tag->count++; + $tag->save(); + + // Add the face + $newFace = ORM::factory("items_face"); + $newFace->tag_id = $faceMapping->tag_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + else + { + // Add the face + $newFace = ORM::factory("items_user"); + $newFace->user_id = $faceMapping->user_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + } + + static function parsePicasaIni($filePath) + { + // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it + $ini_lines = file($filePath); + + $curFilename = ""; + + $photosWithFaces = array(); + + foreach ($ini_lines as $ini_line) + { + // Trim off any whitespace at the ends + $ini_line = trim($ini_line); + + if ($ini_line[0] == '[') + { + // If this line starts with [ it's a filename, strip off the brackets + $curFilename = substr($ini_line, 1, -1); + } + else + { + // If this isn't a filename, it must be data for a file, get the key/value pair + $photoData = explode("=", $ini_line); + + if ($photoData[0] == "faces") + { + // If it's face data, break it up by face + $faces = explode(";", $photoData[1]); + + $photoFaces = array(); + + foreach ($faces as $face) + { + // Split a face into the rectangle and face id + $splitface = explode(",", $face); + + $hexrect = substr($splitface[0], 7, -1); + // We need a string with 16 chars. Fill up with zeros from left. + $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); + $person = $splitface[1]; + + // The rectangle is 4 4-character hex values + $left = hexdec(substr($hexrect,0,4)); + $top = hexdec(substr($hexrect,4,4)); + $right = hexdec(substr($hexrect,8,4)); + $bottom = hexdec(substr($hexrect,12,4)); + + $facePos = array("left" => $left, + "top" => $top, + "right" => $right, + "bottom" => $bottom); + + $photoFaces[$person] = $facePos; + } + + $photosWithFaces[$curFilename] = $photoFaces; + } + } + } + + return $photosWithFaces; + } +} + +?> diff --git a/3.0/modules/picasa_faces/models/picasa_face.php b/3.0/modules/picasa_faces/models/picasa_face.php new file mode 100644 index 00000000..f70cbc8a --- /dev/null +++ b/3.0/modules/picasa_faces/models/picasa_face.php @@ -0,0 +1,5 @@ + diff --git a/3.0/modules/picasa_faces/module.info b/3.0/modules/picasa_faces/module.info new file mode 100644 index 00000000..9e8cb7e4 --- /dev/null +++ b/3.0/modules/picasa_faces/module.info @@ -0,0 +1,3 @@ +name = "Picasa Faces" +description = "Import face data from Picasa so it can be used with the Photo Annotation module." +version = 2 From ae78daac3c20ab7500e49baba8de594f43667f5e Mon Sep 17 00:00:00 2001 From: colings Date: Sat, 1 Jan 2011 12:14:59 -0600 Subject: [PATCH 193/300] Reformatted to match gallery core standards, removed a temp file --- .../helpers/picasa_faces_event.php | 53 +- .../helpers/picasa_faces_installer.php | 97 +-- .../helpers/picasa_faces_task.php | 550 +++++++++--------- .../helpers/picasa_faces_task.php.bak | 313 ---------- .../picasa_faces/models/picasa_face.php | 21 +- 5 files changed, 373 insertions(+), 661 deletions(-) delete mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_event.php b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php index 29886370..605e286b 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_event.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php @@ -1,22 +1,37 @@ deactivate)) - { - site_status::warning( - t("The Picasa Faces module requires the Photo Annotation module. " . - "Activate the Photo Annotation module now", - array("url" => url::site("admin/modules"))), - "picasa_faces_needs_photoannotation"); - } - else - { - site_status::clear("picasa_faces_needs_photoannotation"); - } +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2010 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class picasa_faces_event_Core { + static function module_change($changes) { + // See if the Photo Annotation module is installed, + // tell the user to install it if it isn't. + if (!module::is_active("photoannotation") || in_array("photoannotation", $changes->deactivate)) { + site_status::warning( + t("The Picasa Faces module requires the Photo Annotation module. " . + "Activate the Photo Annotation module now", + array("url" => url::site("admin/modules"))), + "picasa_faces_needs_photoannotation"); } + else { + site_status::clear("picasa_faces_needs_photoannotation"); + } + } } + +?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php index 571626f3..5e728858 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php @@ -1,50 +1,63 @@ query( + "CREATE TABLE IF NOT EXISTS `picasa_faces` ( + `id` int(9) NOT NULL auto_increment, + `face_id` varchar(16) NOT NULL, + `tag_id` int(9) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY `face_id` (`face_id`,`id`) + ) DEFAULT CHARSET=utf8;" + ); - $db->query( - "CREATE TABLE IF NOT EXISTS `picasa_faces` ( - `id` int(9) NOT NULL auto_increment, - `face_id` varchar(16) NOT NULL, - `tag_id` int(9) NOT NULL, - `user_id` int(9) NOT NULL, - PRIMARY KEY (`id`), - KEY `face_id` (`face_id`,`id`) - ) DEFAULT CHARSET=utf8;" - ); + // Set the module version number. + module::set_version("picasa_faces", 2); + } - // Set the module version number. - module::set_version("picasa_faces", 2); + static function upgrade($version) { + if ($version == 1) { + Database::instance()->query( + "ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL" + ); + + module::set_version("picasa_faces", 2); } + } - static function upgrade($version) - { - if ($version == 1) - { - Database::instance()->query( - "ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL" - ); + static function deactivate() { + // Clear the require photo annototaion message when picasa faces is deactivated. + site_status::clear("picasa_faces_needs_photoannotation"); + } - module::set_version("picasa_faces", 2); - } - } - - static function deactivate() - { - // Clear the require photo annototaion message when picasa faces is deactivated. - site_status::clear("picasa_faces_needs_photoannotation"); - } - - static function uninstall() - { - // Delete the face mapping table before uninstalling. - $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {picasa_faces};"); - module::delete("picasa_faces"); - } + static function uninstall() { + // Delete the face mapping table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {picasa_faces};"); + module::delete("picasa_faces"); + } } + +?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php index e845b593..5413a1f6 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php @@ -1,312 +1,292 @@ callback("picasa_faces_task::import_faces") + ->name(t("Import faces from Picasa")) + ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) + ->severity(log::SUCCESS)); + } -class picasa_faces_task_Core -{ - static function available_tasks() - { - return array(Task_Definition::factory() - ->callback("picasa_faces_task::import_faces") - ->name(t("Import faces from Picasa")) - ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) - ->severity(log::SUCCESS)); + static function import_faces($task) { + if (!module::is_active("photoannotation")) { + $task->done = true; + $task->status = t("Photo Annotation module is inactive, no faces will be imported"); + return; } - static function import_faces($task) - { - if (!module::is_active("photoannotation")) - { - $task->done = true; - $task->status = t("Photo Annotation module is inactive, no faces will be imported"); - return; - } + $start = microtime(true); - $start = microtime(true); + // Figure out the total number of albums in the database. + // If this is the first run, also set last_id and completed to 0. + $total = $task->get("total"); + if (empty($total)) { + $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); + $task->set("last_id", 0); + $task->set("completed", 0); + $task->set("new_faces", 0); + $task->set("old_faces", 0); + } + $last_id = $task->get("last_id"); + $completed = $task->get("completed"); + $new_faces = $task->get("new_faces"); + $old_faces = $task->get("old_faces"); - // Figure out the total number of albums in the database. - // If this is the first run, also set last_id and completed to 0. - $total = $task->get("total"); - if (empty($total)) - { - $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); - $task->set("last_id", 0); - $task->set("completed", 0); - $task->set("new_faces", 0); - $task->set("old_faces", 0); - } - $last_id = $task->get("last_id"); - $completed = $task->get("completed"); - $new_faces = $task->get("new_faces"); - $old_faces = $task->get("old_faces"); - - // Try to find a contacts.xml file, and parse out the contents if it exists - $contacts = null; - $contactsXML = VARPATH . "albums/contacts.xml"; - if (file_exists($contactsXML)) - { - $xml = simplexml_load_file($contactsXML); - $contacts = $xml->contact; - } - - // Check each album in the database to see if it has a .picasa.ini file on disk, - // and extract any faces if it does. - foreach (ORM::factory("item") - ->where("id", ">", $last_id) - ->where("type", "=", "album") - ->find_all(100) as $albumItem) - { - $picasaFile = $albumItem->file_path()."/.picasa.ini"; - if (file_exists($picasaFile)) - { - // Parse the .picasa.ini file and extract any faces - $photosWithFaces = self::parsePicasaIni($picasaFile); - - // Build a mapping from photo filenames in this album to the items - $photos = array(); - foreach ($albumItem->children() as $child) - { - if ($child->is_photo()) - { - $photos[$child->name] = $child; - } - } - - // Iterate through all the photos with faces in them - foreach ($photosWithFaces as $photoName => $faces) - { - // Find the item for this photo - $photoItem = $photos[$photoName]; - if ($photoItem) - { - foreach ($faces as $faceId => $faceCoords) - { - $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); - - // If we don't already have a mapping for this face, create one - if (!$faceMapping->loaded()) - { - $newTagId = self::getFaceMapping($faceId, $contacts); - - // Save the mapping from Picasa face id to tag id, so - // faces will be grouped properly - $faceMapping->face_id = $faceId; - $faceMapping->tag_id = $newTagId; - $faceMapping->user_id = 0; - $faceMapping->save(); - } - - if ($faceMapping->user_id == 0) - { - $numTagsOnPhoto = ORM::factory("items_face") - ->where("tag_id", "=", $faceMapping->tag_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - else - { - $numTagsOnPhoto = ORM::factory("items_user") - ->where("user_id", "=", $faceMapping->user_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - - // If this face tag isn't already on this photo, add it (we - // assume a face should only ever appear once per photo) - if ($numTagsOnPhoto == 0) - { - self::addNewFace($faceMapping, $faceCoords, $photoItem); - $new_faces++; - } - else - { - $old_faces++; - } - } - } - } - } - - $last_id = $albumItem->id; - $completed++; - - if ($completed == $total || microtime(true) - $start > 1.5) - { - break; - } - } - - $task->set("completed", $completed); - $task->set("last_id", $last_id); - $task->set("new_faces", $new_faces); - $task->set("old_faces", $old_faces); - - if ($total == $completed) - { - $task->done = true; - $task->state = "success"; - $task->percent_complete = 100; - } - else - { - $task->percent_complete = round(100 * $completed / $total); - } - - $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", - array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + // Try to find a contacts.xml file, and parse out the contents if it exists + $contacts = null; + $contactsXML = VARPATH . "albums/contacts.xml"; + if (file_exists($contactsXML)) { + $xml = simplexml_load_file($contactsXML); + $contacts = $xml->contact; } - static function getFaceMapping($faceId, $contacts) - { - $personTag = null; + // Check each album in the database to see if it has a .picasa.ini file on disk, + // and extract any faces if it does. + foreach (ORM::factory("item") + ->where("id", ">", $last_id) + ->where("type", "=", "album") + ->find_all(100) as $albumItem) { + $picasaFile = $albumItem->file_path()."/.picasa.ini"; + if (file_exists($picasaFile)) { + // Parse the .picasa.ini file and extract any faces + $photosWithFaces = self::parsePicasaIni($picasaFile); - // If we have a contacts.xml file, try to find the face id there - if ($contacts != null) - { - foreach ($contacts as $contact) - { - if ($contact['id'] == $faceId) - { - // We found this face id in the contacts.xml. See if a tag already exists. - $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); - - // If the tag doesn't exist already, add it - if (!$personTag->loaded()) - { - $personTag->name = $contact['name']; - $personTag->save(); - } - - break; - } + // Build a mapping from photo filenames in this album to the items + $photos = array(); + foreach ($albumItem->children() as $child) { + if ($child->is_photo()) { + $photos[$child->name] = $child; } } - // We either didn't find the face in contacts.xml, or we don't have contacts.xml. - // Add the face using a generic name. - if ($personTag == null) - { - // Find an unused "Picasa x" tag - $personID = 0; - $personName = ""; - do - { - $personID++; - $personName = "Picasa ".$personID; - $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); - } while ($personTag->loaded()); + // Iterate through all the photos with faces in them + foreach ($photosWithFaces as $photoName => $faces) { + // Find the item for this photo + $photoItem = $photos[$photoName]; + if ($photoItem) { + foreach ($faces as $faceId => $faceCoords) { + $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); - // We found an open name, save it so we can get the id - $personTag->name = $personName; + // If we don't already have a mapping for this face, create one + if (!$faceMapping->loaded()) { + $newTagId = self::getFaceMapping($faceId, $contacts); + + // Save the mapping from Picasa face id to tag id, so + // faces will be grouped properly + $faceMapping->face_id = $faceId; + $faceMapping->tag_id = $newTagId; + $faceMapping->user_id = 0; + $faceMapping->save(); + } + + if ($faceMapping->user_id == 0) { + $numTagsOnPhoto = ORM::factory("items_face") + ->where("tag_id", "=", $faceMapping->tag_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + else { + $numTagsOnPhoto = ORM::factory("items_user") + ->where("user_id", "=", $faceMapping->user_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + + // If this face tag isn't already on this photo, add it (we + // assume a face should only ever appear once per photo) + if ($numTagsOnPhoto == 0) { + self::addNewFace($faceMapping, $faceCoords, $photoItem); + $new_faces++; + } + else { + $old_faces++; + } + } + } + } + } + + $last_id = $albumItem->id; + $completed++; + + if ($completed == $total || microtime(true) - $start > 1.5) { + break; + } + } + + $task->set("completed", $completed); + $task->set("last_id", $last_id); + $task->set("new_faces", $new_faces); + $task->set("old_faces", $old_faces); + + if ($total == $completed) { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + } + else { + $task->percent_complete = round(100 * $completed / $total); + } + + $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", + array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + } + + static function getFaceMapping($faceId, $contacts) { + $personTag = null; + + // If we have a contacts.xml file, try to find the face id there + if ($contacts != null) { + foreach ($contacts as $contact) { + if ($contact['id'] == $faceId) { + // We found this face id in the contacts.xml. See if a tag already exists. + $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); + + // If the tag doesn't exist already, add it + if (!$personTag->loaded()) { + $personTag->name = $contact['name']; $personTag->save(); - } + } - return $personTag->id; + break; + } + } } - static function addNewFace($faceMapping, $faceCoords, $photoItem) - { - // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap - // that to the resize dimensions. - $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); - $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); - $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); - $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); + // We either didn't find the face in contacts.xml, or we don't have contacts.xml. + // Add the face using a generic name. + if ($personTag == null) { + // Find an unused "Picasa x" tag + $personID = 0; + $personName = ""; + do { + $personID++; + $personName = "Picasa ".$personID; + $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); + } while ($personTag->loaded()); - if ($faceMapping->user_id == 0) - { - // Add the photo to this tag - $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); - $tag->add($photoItem); - $tag->count++; - $tag->save(); - - // Add the face - $newFace = ORM::factory("items_face"); - $newFace->tag_id = $faceMapping->tag_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } - else - { - // Add the face - $newFace = ORM::factory("items_user"); - $newFace->user_id = $faceMapping->user_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } + // We found an open name, save it so we can get the id + $personTag->name = $personName; + $personTag->save(); } - static function parsePicasaIni($filePath) - { - // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it - $ini_lines = file($filePath); + return $personTag->id; + } - $curFilename = ""; + static function addNewFace($faceMapping, $faceCoords, $photoItem) { + // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap + // that to the resize dimensions. + $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); + $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); + $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); + $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); - $photosWithFaces = array(); + if ($faceMapping->user_id == 0) { + // Add the photo to this tag + $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); + $tag->add($photoItem); + $tag->count++; + $tag->save(); - foreach ($ini_lines as $ini_line) - { - // Trim off any whitespace at the ends - $ini_line = trim($ini_line); - - if ($ini_line[0] == '[') - { - // If this line starts with [ it's a filename, strip off the brackets - $curFilename = substr($ini_line, 1, -1); - } - else - { - // If this isn't a filename, it must be data for a file, get the key/value pair - $photoData = explode("=", $ini_line); - - if ($photoData[0] == "faces") - { - // If it's face data, break it up by face - $faces = explode(";", $photoData[1]); - - $photoFaces = array(); - - foreach ($faces as $face) - { - // Split a face into the rectangle and face id - $splitface = explode(",", $face); - - $hexrect = substr($splitface[0], 7, -1); - // We need a string with 16 chars. Fill up with zeros from left. - $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); - $person = $splitface[1]; - - // The rectangle is 4 4-character hex values - $left = hexdec(substr($hexrect,0,4)); - $top = hexdec(substr($hexrect,4,4)); - $right = hexdec(substr($hexrect,8,4)); - $bottom = hexdec(substr($hexrect,12,4)); - - $facePos = array("left" => $left, - "top" => $top, - "right" => $right, - "bottom" => $bottom); - - $photoFaces[$person] = $facePos; - } - - $photosWithFaces[$curFilename] = $photoFaces; - } - } - } - - return $photosWithFaces; + // Add the face + $newFace = ORM::factory("items_face"); + $newFace->tag_id = $faceMapping->tag_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); } + else { + // Add the face + $newFace = ORM::factory("items_user"); + $newFace->user_id = $faceMapping->user_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + } + + static function parsePicasaIni($filePath) { + // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it + $ini_lines = file($filePath); + + $curFilename = ""; + + $photosWithFaces = array(); + + foreach ($ini_lines as $ini_line) { + // Trim off any whitespace at the ends + $ini_line = trim($ini_line); + + if ($ini_line[0] == '[') { + // If this line starts with [ it's a filename, strip off the brackets + $curFilename = substr($ini_line, 1, -1); + } + else { + // If this isn't a filename, it must be data for a file, get the key/value pair + $photoData = explode("=", $ini_line); + + if ($photoData[0] == "faces") { + // If it's face data, break it up by face + $faces = explode(";", $photoData[1]); + + $photoFaces = array(); + + foreach ($faces as $face) { + // Split a face into the rectangle and face id + $splitface = explode(",", $face); + + $hexrect = substr($splitface[0], 7, -1); + // We need a string with 16 chars. Fill up with zeros from left. + $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); + $person = $splitface[1]; + + // The rectangle is 4 4-character hex values + $left = hexdec(substr($hexrect,0,4)); + $top = hexdec(substr($hexrect,4,4)); + $right = hexdec(substr($hexrect,8,4)); + $bottom = hexdec(substr($hexrect,12,4)); + + $facePos = array("left" => $left, + "top" => $top, + "right" => $right, + "bottom" => $bottom); + + $photoFaces[$person] = $facePos; + } + + $photosWithFaces[$curFilename] = $photoFaces; + } + } + } + + return $photosWithFaces; + } } ?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak deleted file mode 100644 index 7bdff7a7..00000000 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak +++ /dev/null @@ -1,313 +0,0 @@ -callback("picasa_faces_task::import_faces") - ->name(t("Import faces from Picasa")) - ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) - ->severity(log::SUCCESS)); - } - - static function import_faces($task) - { - if (!module::is_active("photoannotation")) - { - $task->done = true; - $task->status = t("Photo Annotation module is inactive, no faces will be imported"); - return; - } - - $start = microtime(true); - - // Figure out the total number of albums in the database. - // If this is the first run, also set last_id and completed to 0. - $total = $task->get("total"); - if (empty($total)) - { - $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); - $task->set("last_id", 0); - $task->set("completed", 0); - $task->set("new_faces", 0); - $task->set("old_faces", 0); - } - - $last_id = $task->get("last_id"); - $completed = $task->get("completed"); - $new_faces = $task->get("new_faces"); - $old_faces = $task->get("old_faces"); - - // Try to find a contacts.xml file, and parse out the contents if it exists - $contacts = null; - $contactsXML = VARPATH . "albums/contacts.xml"; - if (file_exists($contactsXML)) - { - $xml = simplexml_load_file($contactsXML); - $contacts = $xml->contact; - } - - // Check each album in the database to see if it has a .picasa.ini file on disk, - // and extract any faces if it does. - foreach (ORM::factory("item") - ->where("id", ">", $last_id) - ->where("type", "=", "album") - ->find_all(100) as $albumItem) - { - $picasaFile = $albumItem->file_path()."/.picasa.ini"; - if (file_exists($picasaFile)) - { - // Parse the .picasa.ini file and extract any faces - $photosWithFaces = self::parsePicasaIni($picasaFile); - - // Build a mapping from photo filenames in this album to the items - $photos = array(); - foreach ($albumItem->children() as $child) - { - if ($child->is_photo()) - { - $photos[$child->name] = $child; - } - } - - // Iterate through all the photos with faces in them - foreach ($photosWithFaces as $photoName => $faces) - { - // Find the item for this photo - $photoItem = $photos[$photoName]; - if ($photoItem) - { - foreach ($faces as $faceId => $faceCoords) - { - $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); - - // If we don't already have a mapping for this face, create one - if (!$faceMapping->loaded()) - { - $newTagId = self::getFaceMapping($faceId, $contacts); - - // Save the mapping from Picasa face id to tag id, so - // faces will be grouped properly - $faceMapping->face_id = $faceId; - $faceMapping->tag_id = $newTagId; - $faceMapping->user_id = 0; - $faceMapping->save(); - } - - if ($faceMapping->user_id == 0) - { - $numTagsOnPhoto = ORM::factory("items_face") - ->where("tag_id", "=", $faceMapping->tag_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - else - { - $numTagsOnPhoto = ORM::factory("items_user") - ->where("user_id", "=", $faceMapping->user_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - - // If this face tag isn't already on this photo, add it (we - // assume a face should only ever appear once per photo) - if ($numTagsOnPhoto == 0) - { - self::addNewFace($faceMapping, $faceCoords, $photoItem); - $new_faces++; - } - else - { - $old_faces++; - } - } - } - } - } - - $last_id = $albumItem->id; - $completed++; - - if ($completed == $total || microtime(true) - $start > 1.5) - { - break; - } - } - - $task->set("completed", $completed); - $task->set("last_id", $last_id); - $task->set("new_faces", $new_faces); - $task->set("old_faces", $old_faces); - - if ($total == $completed) - { - $task->done = true; - $task->state = "success"; - $task->percent_complete = 100; - } - else - { - $task->percent_complete = round(100 * $completed / $total); - } - - $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", - array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); - } - - static function getFaceMapping($faceId, $contacts) - { - $personTag = null; - - // If we have a contacts.xml file, try to find the face id there - if ($contacts != null) - { - foreach ($contacts as $contact) - { - if ($contact['id'] == $faceId) - { - // We found this face id in the contacts.xml. See if a tag already exists. - $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); - - // If the tag doesn't exist already, add it - if (!$personTag->loaded()) - { - $personTag->name = $contact['name']; - $personTag->save(); - } - - break; - } - } - } - - // We either didn't find the face in contacts.xml, or we don't have contacts.xml. - // Add the face using a generic name. - if ($personTag == null) - { - // Find an unused "Picasa x" tag - $personID = 0; - $personName = ""; - do - { - $personID++; - $personName = "Picasa ".$personID; - $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); - } while ($personTag->loaded()); - - // We found an open name, save it so we can get the id - $personTag->name = $personName; - $personTag->save(); - } - - return $personTag->id; - } - - static function addNewFace($faceMapping, $faceCoords, $photoItem) - { - // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap - // that to the resize dimensions. - $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); - $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); - $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); - $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); - - if ($faceMapping->user_id == 0) - { - // Add the photo to this tag - $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); - $tag->add($photoItem); - $tag->count++; - $tag->save(); - - // Add the face - $newFace = ORM::factory("items_face"); - $newFace->tag_id = $faceMapping->tag_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } - else - { - // Add the face - $newFace = ORM::factory("items_user"); - $newFace->user_id = $faceMapping->user_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } - } - - static function parsePicasaIni($filePath) - { - // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it - $ini_lines = file($filePath); - - $curFilename = ""; - - $photosWithFaces = array(); - - foreach ($ini_lines as $ini_line) - { - // Trim off any whitespace at the ends - $ini_line = trim($ini_line); - - if ($ini_line[0] == '[') - { - // If this line starts with [ it's a filename, strip off the brackets - $curFilename = substr($ini_line, 1, -1); - } - else - { - // If this isn't a filename, it must be data for a file, get the key/value pair - $photoData = explode("=", $ini_line); - - if ($photoData[0] == "faces") - { - // If it's face data, break it up by face - $faces = explode(";", $photoData[1]); - - $photoFaces = array(); - - foreach ($faces as $face) - { - // Split a face into the rectangle and face id - $splitface = explode(",", $face); - - $hexrect = substr($splitface[0], 7, -1); - // We need a string with 16 chars. Fill up with zeros from left. - $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); - $person = $splitface[1]; - - // The rectangle is 4 4-character hex values - $left = hexdec(substr($hexrect,0,4)); - $top = hexdec(substr($hexrect,4,4)); - $right = hexdec(substr($hexrect,8,4)); - $bottom = hexdec(substr($hexrect,12,4)); - - $facePos = array("left" => $left, - "top" => $top, - "right" => $right, - "bottom" => $bottom); - - $photoFaces[$person] = $facePos; - } - - $photosWithFaces[$curFilename] = $photoFaces; - } - } - } - - return $photosWithFaces; - } -} - -?> diff --git a/3.0/modules/picasa_faces/models/picasa_face.php b/3.0/modules/picasa_faces/models/picasa_face.php index f70cbc8a..12dd6f50 100644 --- a/3.0/modules/picasa_faces/models/picasa_face.php +++ b/3.0/modules/picasa_faces/models/picasa_face.php @@ -1,5 +1,22 @@ From 290e3f220fc59c07563d1740d7eb6b2a2e4a5cb9 Mon Sep 17 00:00:00 2001 From: colings Date: Sat, 1 Jan 2011 12:20:47 -0600 Subject: [PATCH 194/300] Added ignored face fix from rlparadise --- .../picasa_faces/helpers/picasa_faces_task.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php index 5413a1f6..77d435b1 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php @@ -85,6 +85,11 @@ class picasa_faces_task_Core { foreach ($faces as $faceId => $faceCoords) { $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); + // This is a special id Picasa uses for ignored faces, skip it + if ($faceId == "ffffffffffffffff") { + continue; + } + // If we don't already have a mapping for this face, create one if (!$faceMapping->loaded()) { $newTagId = self::getFaceMapping($faceId, $contacts); @@ -242,7 +247,7 @@ class picasa_faces_task_Core { foreach ($ini_lines as $ini_line) { // Trim off any whitespace at the ends $ini_line = trim($ini_line); - + if ($ini_line[0] == '[') { // If this line starts with [ it's a filename, strip off the brackets $curFilename = substr($ini_line, 1, -1); @@ -250,9 +255,9 @@ class picasa_faces_task_Core { else { // If this isn't a filename, it must be data for a file, get the key/value pair $photoData = explode("=", $ini_line); - + if ($photoData[0] == "faces") { - // If it's face data, break it up by face + // If it's face data, break it up by face $faces = explode(";", $photoData[1]); $photoFaces = array(); @@ -279,7 +284,7 @@ class picasa_faces_task_Core { $photoFaces[$person] = $facePos; } - + $photosWithFaces[$curFilename] = $photoFaces; } } From 7f0f2256487bc9a986d6c2ca551b9063cb825bbb Mon Sep 17 00:00:00 2001 From: colings Date: Sat, 1 Jan 2011 13:16:29 -0600 Subject: [PATCH 195/300] Fixed (hopefully) table prefix issue --- 3.0/modules/picasa_faces/helpers/picasa_faces_installer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php index 5e728858..23010af1 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php @@ -23,7 +23,7 @@ class picasa_faces_installer { $db = Database::instance(); $db->query( - "CREATE TABLE IF NOT EXISTS `picasa_faces` ( + "CREATE TABLE IF NOT EXISTS {picasa_faces} ( `id` int(9) NOT NULL auto_increment, `face_id` varchar(16) NOT NULL, `tag_id` int(9) NOT NULL, From 86a988b16cad1edb31bfda0e9fee2093608c5a3d Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 09:30:19 -0600 Subject: [PATCH 196/300] Initial checkin of custom albums (not working) --- .../helpers/custom_albums_event.php | 64 +++++++++++++++++++ .../helpers/custom_albums_graphics.php | 57 +++++++++++++++++ .../helpers/custom_albums_installer.php | 54 ++++++++++++++++ .../custom_albums/models/custom_album.php | 5 ++ 3.0/modules/custom_albums/module.info | 3 + 5 files changed, 183 insertions(+) create mode 100644 3.0/modules/custom_albums/helpers/custom_albums_event.php create mode 100644 3.0/modules/custom_albums/helpers/custom_albums_graphics.php create mode 100644 3.0/modules/custom_albums/helpers/custom_albums_installer.php create mode 100644 3.0/modules/custom_albums/models/custom_album.php create mode 100644 3.0/modules/custom_albums/module.info diff --git a/3.0/modules/custom_albums/helpers/custom_albums_event.php b/3.0/modules/custom_albums/helpers/custom_albums_event.php new file mode 100644 index 00000000..b8dbe5aa --- /dev/null +++ b/3.0/modules/custom_albums/helpers/custom_albums_event.php @@ -0,0 +1,64 @@ +is_album()) { + $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); + + $thumbdata = $form->edit_item->group("custom_album")->label("Custom Album"); + + if ($albumCustom->loaded()) { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)"))->value($albumCustom->thumb_size); + } else { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)")); + } + } + } + + static function item_edit_form_completed($item, $form) { + if ($item->is_album()) { + $thumbChanged = false; + + if ($form->edit_item->custom_album->thumbsize->value == "") { + db::build() + ->delete("custom_album") + ->where("album_id", "=", $item->id) + ->execute(); + } else { + $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); + if (!$albumCustom->loaded()) { + $albumCustom->album_id = $item->id; + } + $albumCustom->thumb_size = $form->edit_item->custom_album->thumbsize->value; + $albumCustom->save(); + + $thumbChanged = true; + } + + if ($thumbChanged) { + db::build() + ->update("items") + ->set("thumb_dirty", 1) + ->where("parent_id", "=", $item->id) + ->execute(); + } + } + } +} diff --git a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php new file mode 100644 index 00000000..5fa6cd49 --- /dev/null +++ b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php @@ -0,0 +1,57 @@ +where("album_id", "=", $options["parent_id"])->find(); + +/* + $dims = getimagesize($input_file); + if (max($dims[0], $dims[1]) < min($options["width"], $options["height"])) { + // Image would get upscaled; do nothing + copy($input_file, $output_file); + } else { + $image = Image::factory($input_file) + ->resize($options["width"], $options["height"], $options["master"]) + ->quality(module::get_var("gallery", "image_quality")); + if (graphics::can("sharpen")) { + $image->sharpen(module::get_var("gallery", "image_sharpen")); + } + $image->save($output_file); + } +*/ + module::event("graphics_resize_completed", $input_file, $output_file, $options); + } +} diff --git a/3.0/modules/custom_albums/helpers/custom_albums_installer.php b/3.0/modules/custom_albums/helpers/custom_albums_installer.php new file mode 100644 index 00000000..477b70e0 --- /dev/null +++ b/3.0/modules/custom_albums/helpers/custom_albums_installer.php @@ -0,0 +1,54 @@ + 0, "height" => 0, "master" => Image::AUTO), + 200); + graphics::add_rule( + "gallery", "resize", "custom_albums::resize", + array("width" => 0, "height" => 0, "master" => Image::AUTO), + 200); + + // Create a table to store custom album info in. + $db = Database::instance(); + + $db->query( + "CREATE TABLE IF NOT EXISTS {custom_albums} ( + `id` int(9) NOT NULL auto_increment, + `album_id` int(9) NOT NULL, + `thumb_size` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY `album_id` (`album_id`,`id`) + ) DEFAULT CHARSET=utf8;" + ); + + module::set_version("custom_albums", 1); + } + + static function uninstall() { + // Delete the custom album table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {custom_albums};"); + module::delete("custom_albums"); + } +} diff --git a/3.0/modules/custom_albums/models/custom_album.php b/3.0/modules/custom_albums/models/custom_album.php new file mode 100644 index 00000000..56dc8661 --- /dev/null +++ b/3.0/modules/custom_albums/models/custom_album.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/3.0/modules/custom_albums/module.info b/3.0/modules/custom_albums/module.info new file mode 100644 index 00000000..1161c533 --- /dev/null +++ b/3.0/modules/custom_albums/module.info @@ -0,0 +1,3 @@ +name = "Custom Albums" +description = "Lets you set custom thumbnail sizes for albums" +version = 1 From fca49d1cac01066b12c2633e4b0ec724677e0bf5 Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 09:47:30 -0600 Subject: [PATCH 197/300] Maybe working? --- .../helpers/custom_albums_graphics.php | 30 +++++++++++-------- .../helpers/custom_albums_installer.php | 4 --- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php index 5fa6cd49..b6d6702a 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php @@ -37,21 +37,25 @@ class custom_albums_graphics_Core { $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $options["parent_id"])->find(); -/* - $dims = getimagesize($input_file); - if (max($dims[0], $dims[1]) < min($options["width"], $options["height"])) { - // Image would get upscaled; do nothing - copy($input_file, $output_file); - } else { - $image = Image::factory($input_file) - ->resize($options["width"], $options["height"], $options["master"]) - ->quality(module::get_var("gallery", "image_quality")); - if (graphics::can("sharpen")) { - $image->sharpen(module::get_var("gallery", "image_sharpen")); + // If this album has custom data, build the thumbnail at the specified size + if ($albumCustom->loaded()) { + $thumb_size = $albumCustom->thumb_size; + + $dims = getimagesize($input_file); + if (max($dims[0], $dims[1]) < $thumb_size) { + // Image would get upscaled; do nothing + copy($input_file, $output_file); + } else { + $image = Image::factory($input_file) + ->resize($thumb_size, $thumb_size, $options["master"]) + ->quality(module::get_var("gallery", "image_quality")); + if (graphics::can("sharpen")) { + $image->sharpen(module::get_var("gallery", "image_sharpen")); + } + $image->save($output_file); } - $image->save($output_file); } -*/ + module::event("graphics_resize_completed", $input_file, $output_file, $options); } } diff --git a/3.0/modules/custom_albums/helpers/custom_albums_installer.php b/3.0/modules/custom_albums/helpers/custom_albums_installer.php index 477b70e0..319912e9 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_installer.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_installer.php @@ -24,10 +24,6 @@ class custom_albums_installer { "gallery", "thumb", "custom_albums::resize", array("width" => 0, "height" => 0, "master" => Image::AUTO), 200); - graphics::add_rule( - "gallery", "resize", "custom_albums::resize", - array("width" => 0, "height" => 0, "master" => Image::AUTO), - 200); // Create a table to store custom album info in. $db = Database::instance(); From f94e71b90b0ef61dfdc27582aa604132e8e7c5da Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 22:19:07 -0600 Subject: [PATCH 198/300] Works, need to fix dirtying on custom size change --- .../helpers/custom_albums_event.php | 23 ++++++---- .../helpers/custom_albums_graphics.php | 41 ++++-------------- .../helpers/custom_albums_installer.php | 42 ++++++++++++++++--- 3 files changed, 58 insertions(+), 48 deletions(-) diff --git a/3.0/modules/custom_albums/helpers/custom_albums_event.php b/3.0/modules/custom_albums/helpers/custom_albums_event.php index b8dbe5aa..3564d410 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_event.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_event.php @@ -18,18 +18,18 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class custom_albums_event_Core { - static function item_edit_form($item, $form) { - if ($item->is_album()) { - $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); + static function item_edit_form($item, $form) { + if ($item->is_album()) { + $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); - $thumbdata = $form->edit_item->group("custom_album")->label("Custom Album"); + $thumbdata = $form->edit_item->group("custom_album")->label("Custom Album"); - if ($albumCustom->loaded()) { - $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)"))->value($albumCustom->thumb_size); - } else { - $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)")); - } + if ($albumCustom->loaded()) { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)"))->value($albumCustom->thumb_size); + } else { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)")); } + } } static function item_edit_form_completed($item, $form) { @@ -61,4 +61,9 @@ class custom_albums_event_Core { } } } + + static function theme_edit_form_completed($form) { + // Update our resize rules, in case the thumbnail or resize size has changed + custom_albums_installer::update_rules(); + } } diff --git a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php index b6d6702a..833e39be 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php @@ -18,44 +18,19 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class custom_albums_graphics_Core { - /** - * Resize an image. Valid options are width, height and master. Master is one of the Image - * master dimension constants. - * - * @param string $input_file - * @param string $output_file - * @param array $options - */ - static function resize($input_file, $output_file, $options) { - graphics::init_toolkit(); - - module::event("graphics_resize", $input_file, $output_file, $options); - - if (@filesize($input_file) == 0) { - throw new Exception("@todo EMPTY_INPUT_FILE"); - } - + static function build_thumb($input_file, $output_file, $options) { $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $options["parent_id"])->find(); // If this album has custom data, build the thumbnail at the specified size if ($albumCustom->loaded()) { - $thumb_size = $albumCustom->thumb_size; - - $dims = getimagesize($input_file); - if (max($dims[0], $dims[1]) < $thumb_size) { - // Image would get upscaled; do nothing - copy($input_file, $output_file); - } else { - $image = Image::factory($input_file) - ->resize($thumb_size, $thumb_size, $options["master"]) - ->quality(module::get_var("gallery", "image_quality")); - if (graphics::can("sharpen")) { - $image->sharpen(module::get_var("gallery", "image_sharpen")); - } - $image->save($output_file); - } + $options["width"] = $albumCustom->thumb_size; + $options["height"] = $albumCustom->thumb_size; } - module::event("graphics_resize_completed", $input_file, $output_file, $options); + gallery_graphics::resize($input_file, $output_file, $options); + } + + static function build_resize($input_file, $output_file, $options) { + gallery_graphics::resize($input_file, $output_file, $options); } } diff --git a/3.0/modules/custom_albums/helpers/custom_albums_installer.php b/3.0/modules/custom_albums/helpers/custom_albums_installer.php index 319912e9..b019214e 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_installer.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_installer.php @@ -19,12 +19,6 @@ */ class custom_albums_installer { static function install() { - // Add rules for generating our thumbnails and resizes - graphics::add_rule( - "gallery", "thumb", "custom_albums::resize", - array("width" => 0, "height" => 0, "master" => Image::AUTO), - 200); - // Create a table to store custom album info in. $db = Database::instance(); @@ -38,6 +32,8 @@ class custom_albums_installer { ) DEFAULT CHARSET=utf8;" ); + custom_albums_installer::update_rules(); + module::set_version("custom_albums", 1); } @@ -47,4 +43,38 @@ class custom_albums_installer { $db->query("DROP TABLE IF EXISTS {custom_albums};"); module::delete("custom_albums"); } + + static function update_rules() { + // Make sure our thumb size matches the gallery one + $thumb_size = module::get_var("gallery", "thumb_size"); + if ($thumb_size != module::get_var("custom_albums", "thumb_size")) { + // Remove and readd our rule with the latest thumb size + graphics::remove_rule("custom_albums", "thumb", "custom_albums_graphics::build_thumb"); + graphics::add_rule( + "custom_albums", "thumb", "custom_albums_graphics::build_thumb", + array("width" => $thumb_size, "height" => $thumb_size, "master" => Image::AUTO), + 101); + + // Deactivate the gallery thumbnail generation, we'll handle it now + graphics::deactivate_rules("gallery"); + + module::set_var("custom_albums", "thumb_size", $thumb_size); + } + + // Make sure our resize size matches the gallery one + $resize_size = module::get_var("gallery", "resize_size"); + if ($resize_size != module::get_var("custom_albums", "resize_size")) { + // Remove and readd our rule with the latest resize size + graphics::remove_rule("custom_albums", "resize", "custom_albums_graphics::build_resize"); + graphics::add_rule( + "custom_albums", "resize", "custom_albums_graphics::build_resize", + array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), + 101); + + // Deactivate the gallery resize, we'll handle it now + graphics::deactivate_rules("gallery"); + + module::set_var("custom_albums", "resize_size", $resize_size); + } + } } From 36375b226ce206dbdb1c3685f698edea23c09829 Mon Sep 17 00:00:00 2001 From: undagiga Date: Sat, 1 Jan 2011 16:19:23 +0800 Subject: [PATCH 199/300] Changes by Undagiga to add selected IPTC fields --- .../helpers/about_this_photo_block.php | 23 +++++++++- 3.1/modules/about_this_photo/module.info | 2 +- .../views/about_this_photo.html.php | 45 +++++++++++++------ 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/3.1/modules/about_this_photo/helpers/about_this_photo_block.php b/3.1/modules/about_this_photo/helpers/about_this_photo_block.php index c1adb174..86869844 100644 --- a/3.1/modules/about_this_photo/helpers/about_this_photo_block.php +++ b/3.1/modules/about_this_photo/helpers/about_this_photo_block.php @@ -27,6 +27,10 @@ class about_this_photo_block_Core { $block = new Block(); switch ($block_id) { case "simple": + $item = $theme->item; + if ((!$item) or (!$item->is_photo())) { + return ""; + } $block->css_id = "g-about-this-photo"; $block->title = t("About this photo"); $block->content = new View("about_this_photo.html"); @@ -37,11 +41,25 @@ class about_this_photo_block_Core { if ($exif->loaded()) { $exif = unserialize($exif->data); $timestamp = strtotime($exif["DateTime"]); - $block->content->date = gallery::date($timestamp); + //$block->content->date = gallery::date($timestamp); + $block->content->date = date('D j M Y', $timestamp); $block->content->time = gallery::time($timestamp); } } + $block->content->vcount = $theme->item()->view_count; + + // IPTC - copied more or less from iptc.php + if (module::is_active("iptc")) { + $record = ORM::factory("iptc_record")->where("item_id", "=", $theme->item()->id)->find(); + if ($record->loaded()) { + $record = unserialize($record->data); + $block->content->source = $record["Source"]; + $block->content->caption = $record["Caption"]; + + } + } + if (module::is_active("tag")) { $block->content->tags = tag::item_tags($theme->item()); } @@ -49,4 +67,5 @@ class about_this_photo_block_Core { } return $block; } -} \ No newline at end of file +} + diff --git a/3.1/modules/about_this_photo/module.info b/3.1/modules/about_this_photo/module.info index 6943685e..632c0f05 100644 --- a/3.1/modules/about_this_photo/module.info +++ b/3.1/modules/about_this_photo/module.info @@ -1,3 +1,3 @@ name = "About this Photo" description = "Show some simple, specific and useful info about a given photo" -version = 1 +version = 2 diff --git a/3.1/modules/about_this_photo/views/about_this_photo.html.php b/3.1/modules/about_this_photo/views/about_this_photo.html.php index aa91c578..45db0274 100644 --- a/3.1/modules/about_this_photo/views/about_this_photo.html.php +++ b/3.1/modules/about_this_photo/views/about_this_photo.html.php @@ -1,17 +1,34 @@ - +
                      + +
                      From 668a453be4f858018de0c8a368d4251fabba08f6 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Mon, 3 Jan 2011 12:31:13 -0800 Subject: [PATCH 200/300] Copy 3.1 about_this_photo changes by undagiga over to 3.0 version --- .../helpers/about_this_photo_block.php | 23 +++++++++- 3.0/modules/about_this_photo/module.info | 2 +- .../views/about_this_photo.html.php | 45 +++++++++++++------ 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/3.0/modules/about_this_photo/helpers/about_this_photo_block.php b/3.0/modules/about_this_photo/helpers/about_this_photo_block.php index c1adb174..86869844 100644 --- a/3.0/modules/about_this_photo/helpers/about_this_photo_block.php +++ b/3.0/modules/about_this_photo/helpers/about_this_photo_block.php @@ -27,6 +27,10 @@ class about_this_photo_block_Core { $block = new Block(); switch ($block_id) { case "simple": + $item = $theme->item; + if ((!$item) or (!$item->is_photo())) { + return ""; + } $block->css_id = "g-about-this-photo"; $block->title = t("About this photo"); $block->content = new View("about_this_photo.html"); @@ -37,11 +41,25 @@ class about_this_photo_block_Core { if ($exif->loaded()) { $exif = unserialize($exif->data); $timestamp = strtotime($exif["DateTime"]); - $block->content->date = gallery::date($timestamp); + //$block->content->date = gallery::date($timestamp); + $block->content->date = date('D j M Y', $timestamp); $block->content->time = gallery::time($timestamp); } } + $block->content->vcount = $theme->item()->view_count; + + // IPTC - copied more or less from iptc.php + if (module::is_active("iptc")) { + $record = ORM::factory("iptc_record")->where("item_id", "=", $theme->item()->id)->find(); + if ($record->loaded()) { + $record = unserialize($record->data); + $block->content->source = $record["Source"]; + $block->content->caption = $record["Caption"]; + + } + } + if (module::is_active("tag")) { $block->content->tags = tag::item_tags($theme->item()); } @@ -49,4 +67,5 @@ class about_this_photo_block_Core { } return $block; } -} \ No newline at end of file +} + diff --git a/3.0/modules/about_this_photo/module.info b/3.0/modules/about_this_photo/module.info index 6943685e..632c0f05 100644 --- a/3.0/modules/about_this_photo/module.info +++ b/3.0/modules/about_this_photo/module.info @@ -1,3 +1,3 @@ name = "About this Photo" description = "Show some simple, specific and useful info about a given photo" -version = 1 +version = 2 diff --git a/3.0/modules/about_this_photo/views/about_this_photo.html.php b/3.0/modules/about_this_photo/views/about_this_photo.html.php index aa91c578..45db0274 100644 --- a/3.0/modules/about_this_photo/views/about_this_photo.html.php +++ b/3.0/modules/about_this_photo/views/about_this_photo.html.php @@ -1,17 +1,34 @@ - +
                      + +
                    From 6383a1c0750b0b260341a234d67df4a50e8ae20a Mon Sep 17 00:00:00 2001 From: Beckett Madden-Woods Date: Tue, 4 Jan 2011 04:26:28 +0800 Subject: [PATCH 201/300] Add tag support to captionator module. --- .../captionator/controllers/captionator.php | 23 +++++++++++++++++++ .../views/captionator_dialog.html.php | 12 ++++++++++ .../captionator/controllers/captionator.php | 23 +++++++++++++++++++ .../views/captionator_dialog.html.php | 12 ++++++++++ 4 files changed, 70 insertions(+) diff --git a/3.0/modules/captionator/controllers/captionator.php b/3.0/modules/captionator/controllers/captionator.php index 007ee86f..231f4248 100644 --- a/3.0/modules/captionator/controllers/captionator.php +++ b/3.0/modules/captionator/controllers/captionator.php @@ -30,6 +30,18 @@ class Captionator_Controller extends Controller { $v = new Theme_View("page.html", "collection", "captionator"); $v->content = new View("captionator_dialog.html"); $v->content->album = $album; + $v->content->enable_tags = module::is_active("tag"); + if ($v->content->enable_tags) { + $v->content->tags = array(); + foreach ($album->viewable()->children() as $child) { + $item = ORM::factory("item", $child->id); + $tag_names = array(); + foreach (tag::item_tags($item) as $tag) { + $tag_names[] = $tag->name; + } + $v->content->tags[$child->id] = implode(", ", $tag_names); + } + } print $v; } @@ -42,12 +54,23 @@ class Captionator_Controller extends Controller { if (Input::instance()->post("save")) { $titles = Input::instance()->post("title"); $descriptions = Input::instance()->post("description"); + $tags = Input::instance()->post("tags"); + $enable_tags = module::is_active("tag"); foreach (array_keys($titles) as $id) { $item = ORM::factory("item", $id); if ($item->loaded() && access::can("edit", $item)) { $item->title = $titles[$id]; $item->description = $descriptions[$id]; $item->save(); + if ($enable_tags) { + tag::clear_all($item); + foreach (explode(",", $tags[$id]) as $tag_name) { + if ($tag_name) { + tag::add($item, trim($tag_name)); + } + } + tag::compact(); + } } } message::success(t("Captions saved")); diff --git a/3.0/modules/captionator/views/captionator_dialog.html.php b/3.0/modules/captionator/views/captionator_dialog.html.php index b74be58d..317873ee 100644 --- a/3.0/modules/captionator/views/captionator_dialog.html.php +++ b/3.0/modules/captionator/views/captionator_dialog.html.php @@ -1,5 +1,11 @@
                    +
                    id}") ?>" method="post" id="g-captionator-form">
                    @@ -23,6 +29,12 @@
                  • + +
                  • + + +
                  • +
                  diff --git a/3.1/modules/captionator/controllers/captionator.php b/3.1/modules/captionator/controllers/captionator.php index 007ee86f..231f4248 100644 --- a/3.1/modules/captionator/controllers/captionator.php +++ b/3.1/modules/captionator/controllers/captionator.php @@ -30,6 +30,18 @@ class Captionator_Controller extends Controller { $v = new Theme_View("page.html", "collection", "captionator"); $v->content = new View("captionator_dialog.html"); $v->content->album = $album; + $v->content->enable_tags = module::is_active("tag"); + if ($v->content->enable_tags) { + $v->content->tags = array(); + foreach ($album->viewable()->children() as $child) { + $item = ORM::factory("item", $child->id); + $tag_names = array(); + foreach (tag::item_tags($item) as $tag) { + $tag_names[] = $tag->name; + } + $v->content->tags[$child->id] = implode(", ", $tag_names); + } + } print $v; } @@ -42,12 +54,23 @@ class Captionator_Controller extends Controller { if (Input::instance()->post("save")) { $titles = Input::instance()->post("title"); $descriptions = Input::instance()->post("description"); + $tags = Input::instance()->post("tags"); + $enable_tags = module::is_active("tag"); foreach (array_keys($titles) as $id) { $item = ORM::factory("item", $id); if ($item->loaded() && access::can("edit", $item)) { $item->title = $titles[$id]; $item->description = $descriptions[$id]; $item->save(); + if ($enable_tags) { + tag::clear_all($item); + foreach (explode(",", $tags[$id]) as $tag_name) { + if ($tag_name) { + tag::add($item, trim($tag_name)); + } + } + tag::compact(); + } } } message::success(t("Captions saved")); diff --git a/3.1/modules/captionator/views/captionator_dialog.html.php b/3.1/modules/captionator/views/captionator_dialog.html.php index b74be58d..317873ee 100644 --- a/3.1/modules/captionator/views/captionator_dialog.html.php +++ b/3.1/modules/captionator/views/captionator_dialog.html.php @@ -1,5 +1,11 @@
                  + id}") ?>" method="post" id="g-captionator-form">
                  @@ -23,6 +29,12 @@
                • + +
                • + + +
                • +
                From 7aec401592cf7f5abb28add591f7fd82858292ab Mon Sep 17 00:00:00 2001 From: pez252 Date: Mon, 3 Jan 2011 00:43:36 +0800 Subject: [PATCH 202/300] Initial commit of star module. No changes since 11 Dec 2010 forum post. --- 3.0/modules/star/controllers/admin_star.php | 52 ++++++ 3.0/modules/star/controllers/display.php | 94 ++++++++++ 3.0/modules/star/helpers/MY_item.php | 34 ++++ 3.0/modules/star/helpers/star.php | 163 ++++++++++++++++++ 3.0/modules/star/helpers/star_block.php | 51 ++++++ 3.0/modules/star/helpers/star_event.php | 91 ++++++++++ 3.0/modules/star/helpers/star_installer.php | 43 +++++ 3.0/modules/star/helpers/star_theme.php | 23 +++ 3.0/modules/star/models/starred_item.php | 24 +++ 3.0/modules/star/models/starred_only_user.php | 24 +++ 3.0/modules/star/module.info | 3 + 3.0/modules/star/views/admin_star.html.php | 7 + 3.0/modules/star/views/star_block.html.php | 141 +++++++++++++++ 13 files changed, 750 insertions(+) create mode 100644 3.0/modules/star/controllers/admin_star.php create mode 100644 3.0/modules/star/controllers/display.php create mode 100644 3.0/modules/star/helpers/MY_item.php create mode 100644 3.0/modules/star/helpers/star.php create mode 100644 3.0/modules/star/helpers/star_block.php create mode 100644 3.0/modules/star/helpers/star_event.php create mode 100644 3.0/modules/star/helpers/star_installer.php create mode 100755 3.0/modules/star/helpers/star_theme.php create mode 100644 3.0/modules/star/models/starred_item.php create mode 100644 3.0/modules/star/models/starred_only_user.php create mode 100644 3.0/modules/star/module.info create mode 100644 3.0/modules/star/views/admin_star.html.php create mode 100644 3.0/modules/star/views/star_block.html.php diff --git a/3.0/modules/star/controllers/admin_star.php b/3.0/modules/star/controllers/admin_star.php new file mode 100644 index 00000000..983618f2 --- /dev/null +++ b/3.0/modules/star/controllers/admin_star.php @@ -0,0 +1,52 @@ +page_title = t("Star settings"); + $view->content = new View("admin_star.html"); + $view->content->form = $this->_get_admin_form(); + $view->content->title = $view->page_title; + print $view; + } + + public function save() { + access::verify_csrf(); + $form = $this->_get_admin_form(); + $form->validate(); + module::set_var("star", "show", + $form->show->value); + message::success(t("Star settings updated")); + url::redirect("admin/star"); + } + + private function _get_admin_form() { + $form = new Forge("admin/star/save", "", "post", + array("id" => "g-star-admin-form")); + $form->dropdown("show") + ->label(t("Default to showing...")) + ->options(array(0 => "All",1 => "Starred")) + ->selected(module::get_var("star", "show")); + $form->submit("save")->value(t("Save")); + return $form; + } +} diff --git a/3.0/modules/star/controllers/display.php b/3.0/modules/star/controllers/display.php new file mode 100644 index 00000000..cf2fe805 --- /dev/null +++ b/3.0/modules/star/controllers/display.php @@ -0,0 +1,94 @@ + %title
                item", array("title" => html::purify($item->title))); + + $this->_check_star_permissions($item); + star::star($item); + message::success($msg); + + json::reply(array("result" => "success", "reload" => 1)); + } + + /** + * Allows the given item to be displayed again. + * + * @param int $id the item id + */ + public function unstar($id) { + $item = model_cache::get("item", $id); + $msg = t("Un-starred %title item", array("title" => html::purify($item->title))); + + $this->_check_star_permissions($item); + star::unstar($item); + message::success($msg); + + json::reply(array("result" => "success", "reload" => 1)); + } + + public function star_only_on() { + //$item = model_cache::get("item", $id); + access::verify_csrf(); + $msg = t("Showing starred items."); + + //$this->_check_star_permissions($item); + star::star_only_on(); + message::success($msg); + + json::reply(array("result" => "success", "reload" => 1)); + } + +public function star_only_off() { + //$item = model_cache::get("item", $id); + access::verify_csrf(); + $msg = t("Showing all items."); + + //$this->_check_star_permissions($item); + star::star_only_off(); + message::success($msg); + + json::reply(array("result" => "success", "reload" => 1)); + } + + /** + * Checks whether the given object can be starred by the active user. + * + * @param Item_Model $item the item + */ + private function _check_star_permissions(Item_Model $item) { + access::verify_csrf(); + + access::required("view", $item); + access::required("edit", $item); + + if (!star::can_star()) { + access::forbidden(); + } + } +} diff --git a/3.0/modules/star/helpers/MY_item.php b/3.0/modules/star/helpers/MY_item.php new file mode 100644 index 00000000..281ea650 --- /dev/null +++ b/3.0/modules/star/helpers/MY_item.php @@ -0,0 +1,34 @@ +join("starred_items", "items.id", "starred_items.item_id", "LEFT OUTER") + ->and_where("starred_items.item_id", "IS", TRUE); + } + + return $model; + } +} diff --git a/3.0/modules/star/helpers/star.php b/3.0/modules/star/helpers/star.php new file mode 100644 index 00000000..db9dc3a1 --- /dev/null +++ b/3.0/modules/star/helpers/star.php @@ -0,0 +1,163 @@ +select_list("id", "name"); + // return array_merge(array(self::NONE => t("Nobody")), $options); + // } + + /** + * Returns the starred_item model related to the given item. + * + * There is an attempt to fetch the model from the database through the model + * cache. If it fails, a new unsaved model is created. + * + * @param Item_Model $item the item + * @return Starred_Item_Model the related starred_item model + */ + static function get_starred_item_model(Item_Model $item) { + try { + $model = model_cache::get("item", $id); + } + catch (Exception $e) { + $model = ORM::factory("starred_item"); + $model->item_id = $item->id; + $model->validate(); + } + + return $model; + } + + static function get_star_user_model() { + $model = ORM::factory("starred_only_user"); + $model->user_id = identity::active_user()->id; + $model->validate(); + return $model; + } + + /** + * Returns whether the given item can be starred. + * + * @param Item_Model $item the item + * @return bool + */ + static function can_be_starred(Item_Model $item) { + if (empty($item)) { + return false; + } + + //if ($item->type == "album") { + // return false; + //} + + return true; + } + + /** + * Returns whether the given item is starred. + * + * @param Item_Model $item the item + * @return bool + */ + static function is_starred(Item_Model $item) { + $model = self::get_starred_item_model($item); + return $model->loaded(); + } + + /** + * Stars the given item. + * + * @param Item_Model $item the item to star + */ + static function star(Item_Model $item) { + if (self::is_starred($item)) { + return; + } + + $starred_item = self::get_starred_item_model($item); + $starred_item->save(); + } + + /** + * Allows the given item to be unstarred. + * + * @param Item_Model $item the item to display + */ + static function unstar(Item_Model $item) { + if (!self::is_starred($item)) { + return; + } + + $starred_item = self::get_starred_item_model($item); + $starred_item->delete(); + } + + static function star_only_on() { + if (self::show_only_starred_items()) { + return; + } + + $star_user = self::get_star_user_model(); + $star_user->save(); + } + static function star_only_off() { + if (!self::show_only_starred_items()) { + return; + } + + $star_user = self::get_star_user_model(); + $star_user->delete(); + } + + /** + * Returns whether the active user shows only starred items. + * + * @return bool + */ + static function show_only_starred_items() { + $model = self::get_star_user_model(); + return $model->loaded(); + } + + /** + * Returns whether the active user can star any items. + * + * @return bool + */ + static function can_star() { + if (identity::active_user()->admin) { + return true; + } + + return false; + } +} diff --git a/3.0/modules/star/helpers/star_block.php b/3.0/modules/star/helpers/star_block.php new file mode 100644 index 00000000..12407900 --- /dev/null +++ b/3.0/modules/star/helpers/star_block.php @@ -0,0 +1,51 @@ + t("Star Item")); + } + + static function get($block_id, $theme) { + $item = $theme->item; + switch ($block_id) { + case "star": + + // If Item is movie then... + //if ($item && $item->is_movie() && access::can("view_full", $item)) { + $block = new Block(); + $block->css_id = "g-star-options"; + $block->title = t("Star"); + $block->content = new View("star_block.html"); + return $block; + //} + + // If Item is photo then... + //if ($item && $item->is_photo() && access::can("view_full", $item)) { + // $block = new Block(); + // $block->css_id = "g-download-fullsize"; + // $block->title = t("Download Photo"); + // $block->content = new View("downloadfullsize_block.html"); + // return $block; + //} + } + return ""; + } + +} diff --git a/3.0/modules/star/helpers/star_event.php b/3.0/modules/star/helpers/star_event.php new file mode 100644 index 00000000..e033c41a --- /dev/null +++ b/3.0/modules/star/helpers/star_event.php @@ -0,0 +1,91 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->label(t("Star")) + ->url(url::site("admin/star"))); + } + + static function site_menu($menu, $theme, $item_css_selector) { + $item = $theme->item(); + + if (!empty($item) && star::can_be_starred($item) && star::can_star($item)) { + $csrf = access::csrf_token(); + $link = self::_get_star_link_data($item); + + $menu->get("options_menu") + ->append(Menu::factory("ajax_link") + ->label($link["text"]) + ->ajax_handler("function(data) { window.location.reload() }") + ->url(url::site("display/".$link["action"]."/$item->id?csrf=$csrf"))); + } + } + + static function context_menu($menu, $theme, $item, $thumb_css_selector) { + if (star::can_be_starred($item) && star::can_star($item)) { + $csrf = access::csrf_token(); + $link = self::_get_star_link_data($item); + + $menu + ->get("options_menu") + ->append(Menu::factory("ajax_link") + ->label($link["text"]) + ->ajax_handler("function(data) { window.location.reload() }") + ->url(url::site("display/".$link["action"]."/$item->id?csrf=$csrf"))); + } + } + + /** + * Returns some data used to create a star link. + * + * @param Item_Model $item the related item + * @return array + */ + private static function _get_star_link_data(Item_Model $item) { + if (star::is_starred($item)) { + $action = "unstar"; + $action_label = "Unstar"; + } + else { + $action = "star"; + $action_label = "Star"; + } + + switch ($item->type) { + case "movie": + $item_type_label = "movie"; + break; + case "album": + $item_type_label = "album"; + break; + default: + $item_type_label = "photo"; + break; + } + + $label = t("$action_label this $item_type_label"); + + return array("text" => $label, "action" => $action); + } +} diff --git a/3.0/modules/star/helpers/star_installer.php b/3.0/modules/star/helpers/star_installer.php new file mode 100644 index 00000000..ab260c67 --- /dev/null +++ b/3.0/modules/star/helpers/star_installer.php @@ -0,0 +1,43 @@ +query("CREATE TABLE IF NOT EXISTS {starred_items} ( + `item_id` int(9) NOT NULL, + PRIMARY KEY (`item_id`)) + DEFAULT CHARSET=utf8;"); + $db->query("CREATE TABLE IF NOT EXISTS {starred_only_users} ( + `user_id` int(9) NOT NULL, + PRIMARY KEY (`user_id`)) + DEFAULT CHARSET=utf8;"); + + module::set_var("star", "access_permissions", 0); + module::set_version("star", 1); + } + + static function uninstall() { + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {starred_items};"); + $db->query("DROP TABLE IF EXISTS {starred_only_users};"); + } +} diff --git a/3.0/modules/star/helpers/star_theme.php b/3.0/modules/star/helpers/star_theme.php new file mode 100755 index 00000000..3a0a345e --- /dev/null +++ b/3.0/modules/star/helpers/star_theme.php @@ -0,0 +1,23 @@ + +
                +

                +
                + +
                +
                diff --git a/3.0/modules/star/views/star_block.html.php b/3.0/modules/star/views/star_block.html.php new file mode 100644 index 00000000..b76c5470 --- /dev/null +++ b/3.0/modules/star/views/star_block.html.php @@ -0,0 +1,141 @@ + + +item->is_photo()) { ?> +item)) { ?> + + + + + + + + +item->is_photo()) { ?> +item)) { ?> + + + + + + + + + +item)) { ?> + + + + + + + + + + + +
                + + + +" + class="g-button ui-icon-left ui-state-default ui-corner-all"> +
                + + + + +
                + + + +" + class="g-button ui-icon-left ui-state-default ui-corner-all"> +
                + + + + + + + + From 7fafd71fb4ef7f0ef3b18b23a2ddeb08033ff34a Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 01:26:39 +0800 Subject: [PATCH 203/300] Initial add of Picasa Faces module --- .../helpers/picasa_faces_event.php | 22 ++ .../helpers/picasa_faces_installer.php | 50 +++ .../helpers/picasa_faces_task.php | 312 +++++++++++++++++ .../helpers/picasa_faces_task.php.bak | 313 ++++++++++++++++++ .../picasa_faces/models/picasa_face.php | 5 + 3.0/modules/picasa_faces/module.info | 3 + 6 files changed, 705 insertions(+) create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_event.php create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_installer.php create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_task.php create mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak create mode 100644 3.0/modules/picasa_faces/models/picasa_face.php create mode 100644 3.0/modules/picasa_faces/module.info diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_event.php b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php new file mode 100644 index 00000000..29886370 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php @@ -0,0 +1,22 @@ +deactivate)) + { + site_status::warning( + t("The Picasa Faces module requires the Photo Annotation module. " . + "Activate the Photo Annotation module now", + array("url" => url::site("admin/modules"))), + "picasa_faces_needs_photoannotation"); + } + else + { + site_status::clear("picasa_faces_needs_photoannotation"); + } + } +} diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php new file mode 100644 index 00000000..571626f3 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php @@ -0,0 +1,50 @@ +query( + "CREATE TABLE IF NOT EXISTS `picasa_faces` ( + `id` int(9) NOT NULL auto_increment, + `face_id` varchar(16) NOT NULL, + `tag_id` int(9) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY `face_id` (`face_id`,`id`) + ) DEFAULT CHARSET=utf8;" + ); + + // Set the module version number. + module::set_version("picasa_faces", 2); + } + + static function upgrade($version) + { + if ($version == 1) + { + Database::instance()->query( + "ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL" + ); + + module::set_version("picasa_faces", 2); + } + } + + static function deactivate() + { + // Clear the require photo annototaion message when picasa faces is deactivated. + site_status::clear("picasa_faces_needs_photoannotation"); + } + + static function uninstall() + { + // Delete the face mapping table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {picasa_faces};"); + module::delete("picasa_faces"); + } +} diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php new file mode 100644 index 00000000..e845b593 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php @@ -0,0 +1,312 @@ +callback("picasa_faces_task::import_faces") + ->name(t("Import faces from Picasa")) + ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) + ->severity(log::SUCCESS)); + } + + static function import_faces($task) + { + if (!module::is_active("photoannotation")) + { + $task->done = true; + $task->status = t("Photo Annotation module is inactive, no faces will be imported"); + return; + } + + $start = microtime(true); + + // Figure out the total number of albums in the database. + // If this is the first run, also set last_id and completed to 0. + $total = $task->get("total"); + if (empty($total)) + { + $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); + $task->set("last_id", 0); + $task->set("completed", 0); + $task->set("new_faces", 0); + $task->set("old_faces", 0); + } + $last_id = $task->get("last_id"); + $completed = $task->get("completed"); + $new_faces = $task->get("new_faces"); + $old_faces = $task->get("old_faces"); + + // Try to find a contacts.xml file, and parse out the contents if it exists + $contacts = null; + $contactsXML = VARPATH . "albums/contacts.xml"; + if (file_exists($contactsXML)) + { + $xml = simplexml_load_file($contactsXML); + $contacts = $xml->contact; + } + + // Check each album in the database to see if it has a .picasa.ini file on disk, + // and extract any faces if it does. + foreach (ORM::factory("item") + ->where("id", ">", $last_id) + ->where("type", "=", "album") + ->find_all(100) as $albumItem) + { + $picasaFile = $albumItem->file_path()."/.picasa.ini"; + if (file_exists($picasaFile)) + { + // Parse the .picasa.ini file and extract any faces + $photosWithFaces = self::parsePicasaIni($picasaFile); + + // Build a mapping from photo filenames in this album to the items + $photos = array(); + foreach ($albumItem->children() as $child) + { + if ($child->is_photo()) + { + $photos[$child->name] = $child; + } + } + + // Iterate through all the photos with faces in them + foreach ($photosWithFaces as $photoName => $faces) + { + // Find the item for this photo + $photoItem = $photos[$photoName]; + if ($photoItem) + { + foreach ($faces as $faceId => $faceCoords) + { + $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); + + // If we don't already have a mapping for this face, create one + if (!$faceMapping->loaded()) + { + $newTagId = self::getFaceMapping($faceId, $contacts); + + // Save the mapping from Picasa face id to tag id, so + // faces will be grouped properly + $faceMapping->face_id = $faceId; + $faceMapping->tag_id = $newTagId; + $faceMapping->user_id = 0; + $faceMapping->save(); + } + + if ($faceMapping->user_id == 0) + { + $numTagsOnPhoto = ORM::factory("items_face") + ->where("tag_id", "=", $faceMapping->tag_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + else + { + $numTagsOnPhoto = ORM::factory("items_user") + ->where("user_id", "=", $faceMapping->user_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + + // If this face tag isn't already on this photo, add it (we + // assume a face should only ever appear once per photo) + if ($numTagsOnPhoto == 0) + { + self::addNewFace($faceMapping, $faceCoords, $photoItem); + $new_faces++; + } + else + { + $old_faces++; + } + } + } + } + } + + $last_id = $albumItem->id; + $completed++; + + if ($completed == $total || microtime(true) - $start > 1.5) + { + break; + } + } + + $task->set("completed", $completed); + $task->set("last_id", $last_id); + $task->set("new_faces", $new_faces); + $task->set("old_faces", $old_faces); + + if ($total == $completed) + { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + } + else + { + $task->percent_complete = round(100 * $completed / $total); + } + + $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", + array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + } + + static function getFaceMapping($faceId, $contacts) + { + $personTag = null; + + // If we have a contacts.xml file, try to find the face id there + if ($contacts != null) + { + foreach ($contacts as $contact) + { + if ($contact['id'] == $faceId) + { + // We found this face id in the contacts.xml. See if a tag already exists. + $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); + + // If the tag doesn't exist already, add it + if (!$personTag->loaded()) + { + $personTag->name = $contact['name']; + $personTag->save(); + } + + break; + } + } + } + + // We either didn't find the face in contacts.xml, or we don't have contacts.xml. + // Add the face using a generic name. + if ($personTag == null) + { + // Find an unused "Picasa x" tag + $personID = 0; + $personName = ""; + do + { + $personID++; + $personName = "Picasa ".$personID; + $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); + } while ($personTag->loaded()); + + // We found an open name, save it so we can get the id + $personTag->name = $personName; + $personTag->save(); + } + + return $personTag->id; + } + + static function addNewFace($faceMapping, $faceCoords, $photoItem) + { + // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap + // that to the resize dimensions. + $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); + $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); + $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); + $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); + + if ($faceMapping->user_id == 0) + { + // Add the photo to this tag + $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); + $tag->add($photoItem); + $tag->count++; + $tag->save(); + + // Add the face + $newFace = ORM::factory("items_face"); + $newFace->tag_id = $faceMapping->tag_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + else + { + // Add the face + $newFace = ORM::factory("items_user"); + $newFace->user_id = $faceMapping->user_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + } + + static function parsePicasaIni($filePath) + { + // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it + $ini_lines = file($filePath); + + $curFilename = ""; + + $photosWithFaces = array(); + + foreach ($ini_lines as $ini_line) + { + // Trim off any whitespace at the ends + $ini_line = trim($ini_line); + + if ($ini_line[0] == '[') + { + // If this line starts with [ it's a filename, strip off the brackets + $curFilename = substr($ini_line, 1, -1); + } + else + { + // If this isn't a filename, it must be data for a file, get the key/value pair + $photoData = explode("=", $ini_line); + + if ($photoData[0] == "faces") + { + // If it's face data, break it up by face + $faces = explode(";", $photoData[1]); + + $photoFaces = array(); + + foreach ($faces as $face) + { + // Split a face into the rectangle and face id + $splitface = explode(",", $face); + + $hexrect = substr($splitface[0], 7, -1); + // We need a string with 16 chars. Fill up with zeros from left. + $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); + $person = $splitface[1]; + + // The rectangle is 4 4-character hex values + $left = hexdec(substr($hexrect,0,4)); + $top = hexdec(substr($hexrect,4,4)); + $right = hexdec(substr($hexrect,8,4)); + $bottom = hexdec(substr($hexrect,12,4)); + + $facePos = array("left" => $left, + "top" => $top, + "right" => $right, + "bottom" => $bottom); + + $photoFaces[$person] = $facePos; + } + + $photosWithFaces[$curFilename] = $photoFaces; + } + } + } + + return $photosWithFaces; + } +} + +?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak new file mode 100644 index 00000000..7bdff7a7 --- /dev/null +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak @@ -0,0 +1,313 @@ +callback("picasa_faces_task::import_faces") + ->name(t("Import faces from Picasa")) + ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) + ->severity(log::SUCCESS)); + } + + static function import_faces($task) + { + if (!module::is_active("photoannotation")) + { + $task->done = true; + $task->status = t("Photo Annotation module is inactive, no faces will be imported"); + return; + } + + $start = microtime(true); + + // Figure out the total number of albums in the database. + // If this is the first run, also set last_id and completed to 0. + $total = $task->get("total"); + if (empty($total)) + { + $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); + $task->set("last_id", 0); + $task->set("completed", 0); + $task->set("new_faces", 0); + $task->set("old_faces", 0); + } + + $last_id = $task->get("last_id"); + $completed = $task->get("completed"); + $new_faces = $task->get("new_faces"); + $old_faces = $task->get("old_faces"); + + // Try to find a contacts.xml file, and parse out the contents if it exists + $contacts = null; + $contactsXML = VARPATH . "albums/contacts.xml"; + if (file_exists($contactsXML)) + { + $xml = simplexml_load_file($contactsXML); + $contacts = $xml->contact; + } + + // Check each album in the database to see if it has a .picasa.ini file on disk, + // and extract any faces if it does. + foreach (ORM::factory("item") + ->where("id", ">", $last_id) + ->where("type", "=", "album") + ->find_all(100) as $albumItem) + { + $picasaFile = $albumItem->file_path()."/.picasa.ini"; + if (file_exists($picasaFile)) + { + // Parse the .picasa.ini file and extract any faces + $photosWithFaces = self::parsePicasaIni($picasaFile); + + // Build a mapping from photo filenames in this album to the items + $photos = array(); + foreach ($albumItem->children() as $child) + { + if ($child->is_photo()) + { + $photos[$child->name] = $child; + } + } + + // Iterate through all the photos with faces in them + foreach ($photosWithFaces as $photoName => $faces) + { + // Find the item for this photo + $photoItem = $photos[$photoName]; + if ($photoItem) + { + foreach ($faces as $faceId => $faceCoords) + { + $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); + + // If we don't already have a mapping for this face, create one + if (!$faceMapping->loaded()) + { + $newTagId = self::getFaceMapping($faceId, $contacts); + + // Save the mapping from Picasa face id to tag id, so + // faces will be grouped properly + $faceMapping->face_id = $faceId; + $faceMapping->tag_id = $newTagId; + $faceMapping->user_id = 0; + $faceMapping->save(); + } + + if ($faceMapping->user_id == 0) + { + $numTagsOnPhoto = ORM::factory("items_face") + ->where("tag_id", "=", $faceMapping->tag_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + else + { + $numTagsOnPhoto = ORM::factory("items_user") + ->where("user_id", "=", $faceMapping->user_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + + // If this face tag isn't already on this photo, add it (we + // assume a face should only ever appear once per photo) + if ($numTagsOnPhoto == 0) + { + self::addNewFace($faceMapping, $faceCoords, $photoItem); + $new_faces++; + } + else + { + $old_faces++; + } + } + } + } + } + + $last_id = $albumItem->id; + $completed++; + + if ($completed == $total || microtime(true) - $start > 1.5) + { + break; + } + } + + $task->set("completed", $completed); + $task->set("last_id", $last_id); + $task->set("new_faces", $new_faces); + $task->set("old_faces", $old_faces); + + if ($total == $completed) + { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + } + else + { + $task->percent_complete = round(100 * $completed / $total); + } + + $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", + array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + } + + static function getFaceMapping($faceId, $contacts) + { + $personTag = null; + + // If we have a contacts.xml file, try to find the face id there + if ($contacts != null) + { + foreach ($contacts as $contact) + { + if ($contact['id'] == $faceId) + { + // We found this face id in the contacts.xml. See if a tag already exists. + $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); + + // If the tag doesn't exist already, add it + if (!$personTag->loaded()) + { + $personTag->name = $contact['name']; + $personTag->save(); + } + + break; + } + } + } + + // We either didn't find the face in contacts.xml, or we don't have contacts.xml. + // Add the face using a generic name. + if ($personTag == null) + { + // Find an unused "Picasa x" tag + $personID = 0; + $personName = ""; + do + { + $personID++; + $personName = "Picasa ".$personID; + $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); + } while ($personTag->loaded()); + + // We found an open name, save it so we can get the id + $personTag->name = $personName; + $personTag->save(); + } + + return $personTag->id; + } + + static function addNewFace($faceMapping, $faceCoords, $photoItem) + { + // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap + // that to the resize dimensions. + $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); + $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); + $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); + $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); + + if ($faceMapping->user_id == 0) + { + // Add the photo to this tag + $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); + $tag->add($photoItem); + $tag->count++; + $tag->save(); + + // Add the face + $newFace = ORM::factory("items_face"); + $newFace->tag_id = $faceMapping->tag_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + else + { + // Add the face + $newFace = ORM::factory("items_user"); + $newFace->user_id = $faceMapping->user_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + } + + static function parsePicasaIni($filePath) + { + // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it + $ini_lines = file($filePath); + + $curFilename = ""; + + $photosWithFaces = array(); + + foreach ($ini_lines as $ini_line) + { + // Trim off any whitespace at the ends + $ini_line = trim($ini_line); + + if ($ini_line[0] == '[') + { + // If this line starts with [ it's a filename, strip off the brackets + $curFilename = substr($ini_line, 1, -1); + } + else + { + // If this isn't a filename, it must be data for a file, get the key/value pair + $photoData = explode("=", $ini_line); + + if ($photoData[0] == "faces") + { + // If it's face data, break it up by face + $faces = explode(";", $photoData[1]); + + $photoFaces = array(); + + foreach ($faces as $face) + { + // Split a face into the rectangle and face id + $splitface = explode(",", $face); + + $hexrect = substr($splitface[0], 7, -1); + // We need a string with 16 chars. Fill up with zeros from left. + $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); + $person = $splitface[1]; + + // The rectangle is 4 4-character hex values + $left = hexdec(substr($hexrect,0,4)); + $top = hexdec(substr($hexrect,4,4)); + $right = hexdec(substr($hexrect,8,4)); + $bottom = hexdec(substr($hexrect,12,4)); + + $facePos = array("left" => $left, + "top" => $top, + "right" => $right, + "bottom" => $bottom); + + $photoFaces[$person] = $facePos; + } + + $photosWithFaces[$curFilename] = $photoFaces; + } + } + } + + return $photosWithFaces; + } +} + +?> diff --git a/3.0/modules/picasa_faces/models/picasa_face.php b/3.0/modules/picasa_faces/models/picasa_face.php new file mode 100644 index 00000000..f70cbc8a --- /dev/null +++ b/3.0/modules/picasa_faces/models/picasa_face.php @@ -0,0 +1,5 @@ + diff --git a/3.0/modules/picasa_faces/module.info b/3.0/modules/picasa_faces/module.info new file mode 100644 index 00000000..9e8cb7e4 --- /dev/null +++ b/3.0/modules/picasa_faces/module.info @@ -0,0 +1,3 @@ +name = "Picasa Faces" +description = "Import face data from Picasa so it can be used with the Photo Annotation module." +version = 2 From fdce0009fcfbb4fa4ebcc6e0957d654caf6771d2 Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 02:14:59 +0800 Subject: [PATCH 204/300] Reformatted to match gallery core standards, removed a temp file --- .../helpers/picasa_faces_event.php | 53 +- .../helpers/picasa_faces_installer.php | 97 +-- .../helpers/picasa_faces_task.php | 550 +++++++++--------- .../helpers/picasa_faces_task.php.bak | 313 ---------- .../picasa_faces/models/picasa_face.php | 21 +- 5 files changed, 373 insertions(+), 661 deletions(-) delete mode 100644 3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_event.php b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php index 29886370..605e286b 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_event.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_event.php @@ -1,22 +1,37 @@ deactivate)) - { - site_status::warning( - t("The Picasa Faces module requires the Photo Annotation module. " . - "Activate the Photo Annotation module now", - array("url" => url::site("admin/modules"))), - "picasa_faces_needs_photoannotation"); - } - else - { - site_status::clear("picasa_faces_needs_photoannotation"); - } +/** + * Gallery - a web based photo album viewer and editor + * Copyright (C) 2000-2010 Bharat Mediratta + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. + */ +class picasa_faces_event_Core { + static function module_change($changes) { + // See if the Photo Annotation module is installed, + // tell the user to install it if it isn't. + if (!module::is_active("photoannotation") || in_array("photoannotation", $changes->deactivate)) { + site_status::warning( + t("The Picasa Faces module requires the Photo Annotation module. " . + "Activate the Photo Annotation module now", + array("url" => url::site("admin/modules"))), + "picasa_faces_needs_photoannotation"); } + else { + site_status::clear("picasa_faces_needs_photoannotation"); + } + } } + +?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php index 571626f3..5e728858 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php @@ -1,50 +1,63 @@ query( + "CREATE TABLE IF NOT EXISTS `picasa_faces` ( + `id` int(9) NOT NULL auto_increment, + `face_id` varchar(16) NOT NULL, + `tag_id` int(9) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY `face_id` (`face_id`,`id`) + ) DEFAULT CHARSET=utf8;" + ); - $db->query( - "CREATE TABLE IF NOT EXISTS `picasa_faces` ( - `id` int(9) NOT NULL auto_increment, - `face_id` varchar(16) NOT NULL, - `tag_id` int(9) NOT NULL, - `user_id` int(9) NOT NULL, - PRIMARY KEY (`id`), - KEY `face_id` (`face_id`,`id`) - ) DEFAULT CHARSET=utf8;" - ); + // Set the module version number. + module::set_version("picasa_faces", 2); + } - // Set the module version number. - module::set_version("picasa_faces", 2); + static function upgrade($version) { + if ($version == 1) { + Database::instance()->query( + "ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL" + ); + + module::set_version("picasa_faces", 2); } + } - static function upgrade($version) - { - if ($version == 1) - { - Database::instance()->query( - "ALTER TABLE `picasa_faces` ADD `user_id` int(9) NOT NULL" - ); + static function deactivate() { + // Clear the require photo annototaion message when picasa faces is deactivated. + site_status::clear("picasa_faces_needs_photoannotation"); + } - module::set_version("picasa_faces", 2); - } - } - - static function deactivate() - { - // Clear the require photo annototaion message when picasa faces is deactivated. - site_status::clear("picasa_faces_needs_photoannotation"); - } - - static function uninstall() - { - // Delete the face mapping table before uninstalling. - $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {picasa_faces};"); - module::delete("picasa_faces"); - } + static function uninstall() { + // Delete the face mapping table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {picasa_faces};"); + module::delete("picasa_faces"); + } } + +?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php index e845b593..5413a1f6 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php @@ -1,312 +1,292 @@ callback("picasa_faces_task::import_faces") + ->name(t("Import faces from Picasa")) + ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) + ->severity(log::SUCCESS)); + } -class picasa_faces_task_Core -{ - static function available_tasks() - { - return array(Task_Definition::factory() - ->callback("picasa_faces_task::import_faces") - ->name(t("Import faces from Picasa")) - ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) - ->severity(log::SUCCESS)); + static function import_faces($task) { + if (!module::is_active("photoannotation")) { + $task->done = true; + $task->status = t("Photo Annotation module is inactive, no faces will be imported"); + return; } - static function import_faces($task) - { - if (!module::is_active("photoannotation")) - { - $task->done = true; - $task->status = t("Photo Annotation module is inactive, no faces will be imported"); - return; - } + $start = microtime(true); - $start = microtime(true); + // Figure out the total number of albums in the database. + // If this is the first run, also set last_id and completed to 0. + $total = $task->get("total"); + if (empty($total)) { + $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); + $task->set("last_id", 0); + $task->set("completed", 0); + $task->set("new_faces", 0); + $task->set("old_faces", 0); + } + $last_id = $task->get("last_id"); + $completed = $task->get("completed"); + $new_faces = $task->get("new_faces"); + $old_faces = $task->get("old_faces"); - // Figure out the total number of albums in the database. - // If this is the first run, also set last_id and completed to 0. - $total = $task->get("total"); - if (empty($total)) - { - $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); - $task->set("last_id", 0); - $task->set("completed", 0); - $task->set("new_faces", 0); - $task->set("old_faces", 0); - } - $last_id = $task->get("last_id"); - $completed = $task->get("completed"); - $new_faces = $task->get("new_faces"); - $old_faces = $task->get("old_faces"); - - // Try to find a contacts.xml file, and parse out the contents if it exists - $contacts = null; - $contactsXML = VARPATH . "albums/contacts.xml"; - if (file_exists($contactsXML)) - { - $xml = simplexml_load_file($contactsXML); - $contacts = $xml->contact; - } - - // Check each album in the database to see if it has a .picasa.ini file on disk, - // and extract any faces if it does. - foreach (ORM::factory("item") - ->where("id", ">", $last_id) - ->where("type", "=", "album") - ->find_all(100) as $albumItem) - { - $picasaFile = $albumItem->file_path()."/.picasa.ini"; - if (file_exists($picasaFile)) - { - // Parse the .picasa.ini file and extract any faces - $photosWithFaces = self::parsePicasaIni($picasaFile); - - // Build a mapping from photo filenames in this album to the items - $photos = array(); - foreach ($albumItem->children() as $child) - { - if ($child->is_photo()) - { - $photos[$child->name] = $child; - } - } - - // Iterate through all the photos with faces in them - foreach ($photosWithFaces as $photoName => $faces) - { - // Find the item for this photo - $photoItem = $photos[$photoName]; - if ($photoItem) - { - foreach ($faces as $faceId => $faceCoords) - { - $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); - - // If we don't already have a mapping for this face, create one - if (!$faceMapping->loaded()) - { - $newTagId = self::getFaceMapping($faceId, $contacts); - - // Save the mapping from Picasa face id to tag id, so - // faces will be grouped properly - $faceMapping->face_id = $faceId; - $faceMapping->tag_id = $newTagId; - $faceMapping->user_id = 0; - $faceMapping->save(); - } - - if ($faceMapping->user_id == 0) - { - $numTagsOnPhoto = ORM::factory("items_face") - ->where("tag_id", "=", $faceMapping->tag_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - else - { - $numTagsOnPhoto = ORM::factory("items_user") - ->where("user_id", "=", $faceMapping->user_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - - // If this face tag isn't already on this photo, add it (we - // assume a face should only ever appear once per photo) - if ($numTagsOnPhoto == 0) - { - self::addNewFace($faceMapping, $faceCoords, $photoItem); - $new_faces++; - } - else - { - $old_faces++; - } - } - } - } - } - - $last_id = $albumItem->id; - $completed++; - - if ($completed == $total || microtime(true) - $start > 1.5) - { - break; - } - } - - $task->set("completed", $completed); - $task->set("last_id", $last_id); - $task->set("new_faces", $new_faces); - $task->set("old_faces", $old_faces); - - if ($total == $completed) - { - $task->done = true; - $task->state = "success"; - $task->percent_complete = 100; - } - else - { - $task->percent_complete = round(100 * $completed / $total); - } - - $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", - array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + // Try to find a contacts.xml file, and parse out the contents if it exists + $contacts = null; + $contactsXML = VARPATH . "albums/contacts.xml"; + if (file_exists($contactsXML)) { + $xml = simplexml_load_file($contactsXML); + $contacts = $xml->contact; } - static function getFaceMapping($faceId, $contacts) - { - $personTag = null; + // Check each album in the database to see if it has a .picasa.ini file on disk, + // and extract any faces if it does. + foreach (ORM::factory("item") + ->where("id", ">", $last_id) + ->where("type", "=", "album") + ->find_all(100) as $albumItem) { + $picasaFile = $albumItem->file_path()."/.picasa.ini"; + if (file_exists($picasaFile)) { + // Parse the .picasa.ini file and extract any faces + $photosWithFaces = self::parsePicasaIni($picasaFile); - // If we have a contacts.xml file, try to find the face id there - if ($contacts != null) - { - foreach ($contacts as $contact) - { - if ($contact['id'] == $faceId) - { - // We found this face id in the contacts.xml. See if a tag already exists. - $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); - - // If the tag doesn't exist already, add it - if (!$personTag->loaded()) - { - $personTag->name = $contact['name']; - $personTag->save(); - } - - break; - } + // Build a mapping from photo filenames in this album to the items + $photos = array(); + foreach ($albumItem->children() as $child) { + if ($child->is_photo()) { + $photos[$child->name] = $child; } } - // We either didn't find the face in contacts.xml, or we don't have contacts.xml. - // Add the face using a generic name. - if ($personTag == null) - { - // Find an unused "Picasa x" tag - $personID = 0; - $personName = ""; - do - { - $personID++; - $personName = "Picasa ".$personID; - $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); - } while ($personTag->loaded()); + // Iterate through all the photos with faces in them + foreach ($photosWithFaces as $photoName => $faces) { + // Find the item for this photo + $photoItem = $photos[$photoName]; + if ($photoItem) { + foreach ($faces as $faceId => $faceCoords) { + $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); - // We found an open name, save it so we can get the id - $personTag->name = $personName; + // If we don't already have a mapping for this face, create one + if (!$faceMapping->loaded()) { + $newTagId = self::getFaceMapping($faceId, $contacts); + + // Save the mapping from Picasa face id to tag id, so + // faces will be grouped properly + $faceMapping->face_id = $faceId; + $faceMapping->tag_id = $newTagId; + $faceMapping->user_id = 0; + $faceMapping->save(); + } + + if ($faceMapping->user_id == 0) { + $numTagsOnPhoto = ORM::factory("items_face") + ->where("tag_id", "=", $faceMapping->tag_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + else { + $numTagsOnPhoto = ORM::factory("items_user") + ->where("user_id", "=", $faceMapping->user_id) + ->where("item_id", "=", $photoItem->id) + ->count_all(); + } + + // If this face tag isn't already on this photo, add it (we + // assume a face should only ever appear once per photo) + if ($numTagsOnPhoto == 0) { + self::addNewFace($faceMapping, $faceCoords, $photoItem); + $new_faces++; + } + else { + $old_faces++; + } + } + } + } + } + + $last_id = $albumItem->id; + $completed++; + + if ($completed == $total || microtime(true) - $start > 1.5) { + break; + } + } + + $task->set("completed", $completed); + $task->set("last_id", $last_id); + $task->set("new_faces", $new_faces); + $task->set("old_faces", $old_faces); + + if ($total == $completed) { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + } + else { + $task->percent_complete = round(100 * $completed / $total); + } + + $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", + array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); + } + + static function getFaceMapping($faceId, $contacts) { + $personTag = null; + + // If we have a contacts.xml file, try to find the face id there + if ($contacts != null) { + foreach ($contacts as $contact) { + if ($contact['id'] == $faceId) { + // We found this face id in the contacts.xml. See if a tag already exists. + $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); + + // If the tag doesn't exist already, add it + if (!$personTag->loaded()) { + $personTag->name = $contact['name']; $personTag->save(); - } + } - return $personTag->id; + break; + } + } } - static function addNewFace($faceMapping, $faceCoords, $photoItem) - { - // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap - // that to the resize dimensions. - $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); - $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); - $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); - $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); + // We either didn't find the face in contacts.xml, or we don't have contacts.xml. + // Add the face using a generic name. + if ($personTag == null) { + // Find an unused "Picasa x" tag + $personID = 0; + $personName = ""; + do { + $personID++; + $personName = "Picasa ".$personID; + $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); + } while ($personTag->loaded()); - if ($faceMapping->user_id == 0) - { - // Add the photo to this tag - $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); - $tag->add($photoItem); - $tag->count++; - $tag->save(); - - // Add the face - $newFace = ORM::factory("items_face"); - $newFace->tag_id = $faceMapping->tag_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } - else - { - // Add the face - $newFace = ORM::factory("items_user"); - $newFace->user_id = $faceMapping->user_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } + // We found an open name, save it so we can get the id + $personTag->name = $personName; + $personTag->save(); } - static function parsePicasaIni($filePath) - { - // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it - $ini_lines = file($filePath); + return $personTag->id; + } - $curFilename = ""; + static function addNewFace($faceMapping, $faceCoords, $photoItem) { + // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap + // that to the resize dimensions. + $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); + $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); + $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); + $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); - $photosWithFaces = array(); + if ($faceMapping->user_id == 0) { + // Add the photo to this tag + $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); + $tag->add($photoItem); + $tag->count++; + $tag->save(); - foreach ($ini_lines as $ini_line) - { - // Trim off any whitespace at the ends - $ini_line = trim($ini_line); - - if ($ini_line[0] == '[') - { - // If this line starts with [ it's a filename, strip off the brackets - $curFilename = substr($ini_line, 1, -1); - } - else - { - // If this isn't a filename, it must be data for a file, get the key/value pair - $photoData = explode("=", $ini_line); - - if ($photoData[0] == "faces") - { - // If it's face data, break it up by face - $faces = explode(";", $photoData[1]); - - $photoFaces = array(); - - foreach ($faces as $face) - { - // Split a face into the rectangle and face id - $splitface = explode(",", $face); - - $hexrect = substr($splitface[0], 7, -1); - // We need a string with 16 chars. Fill up with zeros from left. - $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); - $person = $splitface[1]; - - // The rectangle is 4 4-character hex values - $left = hexdec(substr($hexrect,0,4)); - $top = hexdec(substr($hexrect,4,4)); - $right = hexdec(substr($hexrect,8,4)); - $bottom = hexdec(substr($hexrect,12,4)); - - $facePos = array("left" => $left, - "top" => $top, - "right" => $right, - "bottom" => $bottom); - - $photoFaces[$person] = $facePos; - } - - $photosWithFaces[$curFilename] = $photoFaces; - } - } - } - - return $photosWithFaces; + // Add the face + $newFace = ORM::factory("items_face"); + $newFace->tag_id = $faceMapping->tag_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); } + else { + // Add the face + $newFace = ORM::factory("items_user"); + $newFace->user_id = $faceMapping->user_id; + $newFace->item_id = $photoItem->id; + $newFace->x1 = $left; + $newFace->y1 = $top; + $newFace->x2 = $right; + $newFace->y2 = $bottom; + $newFace->description = ""; + $newFace->save(); + } + } + + static function parsePicasaIni($filePath) { + // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it + $ini_lines = file($filePath); + + $curFilename = ""; + + $photosWithFaces = array(); + + foreach ($ini_lines as $ini_line) { + // Trim off any whitespace at the ends + $ini_line = trim($ini_line); + + if ($ini_line[0] == '[') { + // If this line starts with [ it's a filename, strip off the brackets + $curFilename = substr($ini_line, 1, -1); + } + else { + // If this isn't a filename, it must be data for a file, get the key/value pair + $photoData = explode("=", $ini_line); + + if ($photoData[0] == "faces") { + // If it's face data, break it up by face + $faces = explode(";", $photoData[1]); + + $photoFaces = array(); + + foreach ($faces as $face) { + // Split a face into the rectangle and face id + $splitface = explode(",", $face); + + $hexrect = substr($splitface[0], 7, -1); + // We need a string with 16 chars. Fill up with zeros from left. + $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); + $person = $splitface[1]; + + // The rectangle is 4 4-character hex values + $left = hexdec(substr($hexrect,0,4)); + $top = hexdec(substr($hexrect,4,4)); + $right = hexdec(substr($hexrect,8,4)); + $bottom = hexdec(substr($hexrect,12,4)); + + $facePos = array("left" => $left, + "top" => $top, + "right" => $right, + "bottom" => $bottom); + + $photoFaces[$person] = $facePos; + } + + $photosWithFaces[$curFilename] = $photoFaces; + } + } + } + + return $photosWithFaces; + } } ?> diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak deleted file mode 100644 index 7bdff7a7..00000000 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php.bak +++ /dev/null @@ -1,313 +0,0 @@ -callback("picasa_faces_task::import_faces") - ->name(t("Import faces from Picasa")) - ->description(t("Scan all albums for Picasa face data and add any faces that don't already exist")) - ->severity(log::SUCCESS)); - } - - static function import_faces($task) - { - if (!module::is_active("photoannotation")) - { - $task->done = true; - $task->status = t("Photo Annotation module is inactive, no faces will be imported"); - return; - } - - $start = microtime(true); - - // Figure out the total number of albums in the database. - // If this is the first run, also set last_id and completed to 0. - $total = $task->get("total"); - if (empty($total)) - { - $task->set("total", $total = count(ORM::factory("item")->where("type", "=", "album")->find_all())); - $task->set("last_id", 0); - $task->set("completed", 0); - $task->set("new_faces", 0); - $task->set("old_faces", 0); - } - - $last_id = $task->get("last_id"); - $completed = $task->get("completed"); - $new_faces = $task->get("new_faces"); - $old_faces = $task->get("old_faces"); - - // Try to find a contacts.xml file, and parse out the contents if it exists - $contacts = null; - $contactsXML = VARPATH . "albums/contacts.xml"; - if (file_exists($contactsXML)) - { - $xml = simplexml_load_file($contactsXML); - $contacts = $xml->contact; - } - - // Check each album in the database to see if it has a .picasa.ini file on disk, - // and extract any faces if it does. - foreach (ORM::factory("item") - ->where("id", ">", $last_id) - ->where("type", "=", "album") - ->find_all(100) as $albumItem) - { - $picasaFile = $albumItem->file_path()."/.picasa.ini"; - if (file_exists($picasaFile)) - { - // Parse the .picasa.ini file and extract any faces - $photosWithFaces = self::parsePicasaIni($picasaFile); - - // Build a mapping from photo filenames in this album to the items - $photos = array(); - foreach ($albumItem->children() as $child) - { - if ($child->is_photo()) - { - $photos[$child->name] = $child; - } - } - - // Iterate through all the photos with faces in them - foreach ($photosWithFaces as $photoName => $faces) - { - // Find the item for this photo - $photoItem = $photos[$photoName]; - if ($photoItem) - { - foreach ($faces as $faceId => $faceCoords) - { - $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); - - // If we don't already have a mapping for this face, create one - if (!$faceMapping->loaded()) - { - $newTagId = self::getFaceMapping($faceId, $contacts); - - // Save the mapping from Picasa face id to tag id, so - // faces will be grouped properly - $faceMapping->face_id = $faceId; - $faceMapping->tag_id = $newTagId; - $faceMapping->user_id = 0; - $faceMapping->save(); - } - - if ($faceMapping->user_id == 0) - { - $numTagsOnPhoto = ORM::factory("items_face") - ->where("tag_id", "=", $faceMapping->tag_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - else - { - $numTagsOnPhoto = ORM::factory("items_user") - ->where("user_id", "=", $faceMapping->user_id) - ->where("item_id", "=", $photoItem->id) - ->count_all(); - } - - // If this face tag isn't already on this photo, add it (we - // assume a face should only ever appear once per photo) - if ($numTagsOnPhoto == 0) - { - self::addNewFace($faceMapping, $faceCoords, $photoItem); - $new_faces++; - } - else - { - $old_faces++; - } - } - } - } - } - - $last_id = $albumItem->id; - $completed++; - - if ($completed == $total || microtime(true) - $start > 1.5) - { - break; - } - } - - $task->set("completed", $completed); - $task->set("last_id", $last_id); - $task->set("new_faces", $new_faces); - $task->set("old_faces", $old_faces); - - if ($total == $completed) - { - $task->done = true; - $task->state = "success"; - $task->percent_complete = 100; - } - else - { - $task->percent_complete = round(100 * $completed / $total); - } - - $task->status = t("%completed / %total albums scanned, %new_faces faces added, %old_faces faces unchanged", - array("completed" => $completed, "total" => $total, "new_faces" => $new_faces, "old_faces" => $old_faces)); - } - - static function getFaceMapping($faceId, $contacts) - { - $personTag = null; - - // If we have a contacts.xml file, try to find the face id there - if ($contacts != null) - { - foreach ($contacts as $contact) - { - if ($contact['id'] == $faceId) - { - // We found this face id in the contacts.xml. See if a tag already exists. - $personTag = ORM::factory("tag")->where("name", "=", $contact['name'])->find(); - - // If the tag doesn't exist already, add it - if (!$personTag->loaded()) - { - $personTag->name = $contact['name']; - $personTag->save(); - } - - break; - } - } - } - - // We either didn't find the face in contacts.xml, or we don't have contacts.xml. - // Add the face using a generic name. - if ($personTag == null) - { - // Find an unused "Picasa x" tag - $personID = 0; - $personName = ""; - do - { - $personID++; - $personName = "Picasa ".$personID; - $personTag = ORM::factory("tag")->where("name", "=", $personName)->find(); - } while ($personTag->loaded()); - - // We found an open name, save it so we can get the id - $personTag->name = $personName; - $personTag->save(); - } - - return $personTag->id; - } - - static function addNewFace($faceMapping, $faceCoords, $photoItem) - { - // Calculate the face coordinates. Picasa stores them as 0-65535, and we remap - // that to the resize dimensions. - $left = (int)(($photoItem->resize_width * $faceCoords["left"]) / 65535); - $top = (int)(($photoItem->resize_height * $faceCoords["top"]) / 65535); - $right = (int)(($photoItem->resize_width * $faceCoords["right"]) / 65535); - $bottom = (int)(($photoItem->resize_height * $faceCoords["bottom"]) / 65535); - - if ($faceMapping->user_id == 0) - { - // Add the photo to this tag - $tag = ORM::factory("tag")->where("id", "=", $faceMapping->tag_id)->find(); - $tag->add($photoItem); - $tag->count++; - $tag->save(); - - // Add the face - $newFace = ORM::factory("items_face"); - $newFace->tag_id = $faceMapping->tag_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } - else - { - // Add the face - $newFace = ORM::factory("items_user"); - $newFace->user_id = $faceMapping->user_id; - $newFace->item_id = $photoItem->id; - $newFace->x1 = $left; - $newFace->y1 = $top; - $newFace->x2 = $right; - $newFace->y2 = $bottom; - $newFace->description = ""; - $newFace->save(); - } - } - - static function parsePicasaIni($filePath) - { - // It would be nice to use parse_ini_file here, but the parens used with the rect64 values break it - $ini_lines = file($filePath); - - $curFilename = ""; - - $photosWithFaces = array(); - - foreach ($ini_lines as $ini_line) - { - // Trim off any whitespace at the ends - $ini_line = trim($ini_line); - - if ($ini_line[0] == '[') - { - // If this line starts with [ it's a filename, strip off the brackets - $curFilename = substr($ini_line, 1, -1); - } - else - { - // If this isn't a filename, it must be data for a file, get the key/value pair - $photoData = explode("=", $ini_line); - - if ($photoData[0] == "faces") - { - // If it's face data, break it up by face - $faces = explode(";", $photoData[1]); - - $photoFaces = array(); - - foreach ($faces as $face) - { - // Split a face into the rectangle and face id - $splitface = explode(",", $face); - - $hexrect = substr($splitface[0], 7, -1); - // We need a string with 16 chars. Fill up with zeros from left. - $hexrect = str_pad($hexrect, 16, "0", STR_PAD_LEFT); - $person = $splitface[1]; - - // The rectangle is 4 4-character hex values - $left = hexdec(substr($hexrect,0,4)); - $top = hexdec(substr($hexrect,4,4)); - $right = hexdec(substr($hexrect,8,4)); - $bottom = hexdec(substr($hexrect,12,4)); - - $facePos = array("left" => $left, - "top" => $top, - "right" => $right, - "bottom" => $bottom); - - $photoFaces[$person] = $facePos; - } - - $photosWithFaces[$curFilename] = $photoFaces; - } - } - } - - return $photosWithFaces; - } -} - -?> diff --git a/3.0/modules/picasa_faces/models/picasa_face.php b/3.0/modules/picasa_faces/models/picasa_face.php index f70cbc8a..12dd6f50 100644 --- a/3.0/modules/picasa_faces/models/picasa_face.php +++ b/3.0/modules/picasa_faces/models/picasa_face.php @@ -1,5 +1,22 @@ From e24704f9b7eeb80ca4adb531b220ea18ebe1cc05 Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 02:20:47 +0800 Subject: [PATCH 205/300] Added ignored face fix from rlparadise --- .../picasa_faces/helpers/picasa_faces_task.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php index 5413a1f6..77d435b1 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_task.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_task.php @@ -85,6 +85,11 @@ class picasa_faces_task_Core { foreach ($faces as $faceId => $faceCoords) { $faceMapping = ORM::factory("picasa_face")->where("face_id", "=", $faceId)->find(); + // This is a special id Picasa uses for ignored faces, skip it + if ($faceId == "ffffffffffffffff") { + continue; + } + // If we don't already have a mapping for this face, create one if (!$faceMapping->loaded()) { $newTagId = self::getFaceMapping($faceId, $contacts); @@ -242,7 +247,7 @@ class picasa_faces_task_Core { foreach ($ini_lines as $ini_line) { // Trim off any whitespace at the ends $ini_line = trim($ini_line); - + if ($ini_line[0] == '[') { // If this line starts with [ it's a filename, strip off the brackets $curFilename = substr($ini_line, 1, -1); @@ -250,9 +255,9 @@ class picasa_faces_task_Core { else { // If this isn't a filename, it must be data for a file, get the key/value pair $photoData = explode("=", $ini_line); - + if ($photoData[0] == "faces") { - // If it's face data, break it up by face + // If it's face data, break it up by face $faces = explode(";", $photoData[1]); $photoFaces = array(); @@ -279,7 +284,7 @@ class picasa_faces_task_Core { $photoFaces[$person] = $facePos; } - + $photosWithFaces[$curFilename] = $photoFaces; } } From 6c29c8229aa18c7ea8c192cfc3918bb8ac4ce128 Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 03:16:29 +0800 Subject: [PATCH 206/300] Fixed (hopefully) table prefix issue --- 3.0/modules/picasa_faces/helpers/picasa_faces_installer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php index 5e728858..23010af1 100644 --- a/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php +++ b/3.0/modules/picasa_faces/helpers/picasa_faces_installer.php @@ -23,7 +23,7 @@ class picasa_faces_installer { $db = Database::instance(); $db->query( - "CREATE TABLE IF NOT EXISTS `picasa_faces` ( + "CREATE TABLE IF NOT EXISTS {picasa_faces} ( `id` int(9) NOT NULL auto_increment, `face_id` varchar(16) NOT NULL, `tag_id` int(9) NOT NULL, From 9f24bc9df6ed73e921d08148f5821d3f4e702d73 Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 23:30:19 +0800 Subject: [PATCH 207/300] Initial checkin of custom albums (not working) --- .../helpers/custom_albums_event.php | 64 +++++++++++++++++++ .../helpers/custom_albums_graphics.php | 57 +++++++++++++++++ .../helpers/custom_albums_installer.php | 54 ++++++++++++++++ .../custom_albums/models/custom_album.php | 5 ++ 3.0/modules/custom_albums/module.info | 3 + 5 files changed, 183 insertions(+) create mode 100644 3.0/modules/custom_albums/helpers/custom_albums_event.php create mode 100644 3.0/modules/custom_albums/helpers/custom_albums_graphics.php create mode 100644 3.0/modules/custom_albums/helpers/custom_albums_installer.php create mode 100644 3.0/modules/custom_albums/models/custom_album.php create mode 100644 3.0/modules/custom_albums/module.info diff --git a/3.0/modules/custom_albums/helpers/custom_albums_event.php b/3.0/modules/custom_albums/helpers/custom_albums_event.php new file mode 100644 index 00000000..b8dbe5aa --- /dev/null +++ b/3.0/modules/custom_albums/helpers/custom_albums_event.php @@ -0,0 +1,64 @@ +is_album()) { + $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); + + $thumbdata = $form->edit_item->group("custom_album")->label("Custom Album"); + + if ($albumCustom->loaded()) { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)"))->value($albumCustom->thumb_size); + } else { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)")); + } + } + } + + static function item_edit_form_completed($item, $form) { + if ($item->is_album()) { + $thumbChanged = false; + + if ($form->edit_item->custom_album->thumbsize->value == "") { + db::build() + ->delete("custom_album") + ->where("album_id", "=", $item->id) + ->execute(); + } else { + $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); + if (!$albumCustom->loaded()) { + $albumCustom->album_id = $item->id; + } + $albumCustom->thumb_size = $form->edit_item->custom_album->thumbsize->value; + $albumCustom->save(); + + $thumbChanged = true; + } + + if ($thumbChanged) { + db::build() + ->update("items") + ->set("thumb_dirty", 1) + ->where("parent_id", "=", $item->id) + ->execute(); + } + } + } +} diff --git a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php new file mode 100644 index 00000000..5fa6cd49 --- /dev/null +++ b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php @@ -0,0 +1,57 @@ +where("album_id", "=", $options["parent_id"])->find(); + +/* + $dims = getimagesize($input_file); + if (max($dims[0], $dims[1]) < min($options["width"], $options["height"])) { + // Image would get upscaled; do nothing + copy($input_file, $output_file); + } else { + $image = Image::factory($input_file) + ->resize($options["width"], $options["height"], $options["master"]) + ->quality(module::get_var("gallery", "image_quality")); + if (graphics::can("sharpen")) { + $image->sharpen(module::get_var("gallery", "image_sharpen")); + } + $image->save($output_file); + } +*/ + module::event("graphics_resize_completed", $input_file, $output_file, $options); + } +} diff --git a/3.0/modules/custom_albums/helpers/custom_albums_installer.php b/3.0/modules/custom_albums/helpers/custom_albums_installer.php new file mode 100644 index 00000000..477b70e0 --- /dev/null +++ b/3.0/modules/custom_albums/helpers/custom_albums_installer.php @@ -0,0 +1,54 @@ + 0, "height" => 0, "master" => Image::AUTO), + 200); + graphics::add_rule( + "gallery", "resize", "custom_albums::resize", + array("width" => 0, "height" => 0, "master" => Image::AUTO), + 200); + + // Create a table to store custom album info in. + $db = Database::instance(); + + $db->query( + "CREATE TABLE IF NOT EXISTS {custom_albums} ( + `id` int(9) NOT NULL auto_increment, + `album_id` int(9) NOT NULL, + `thumb_size` int(9) NOT NULL, + PRIMARY KEY (`id`), + KEY `album_id` (`album_id`,`id`) + ) DEFAULT CHARSET=utf8;" + ); + + module::set_version("custom_albums", 1); + } + + static function uninstall() { + // Delete the custom album table before uninstalling. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {custom_albums};"); + module::delete("custom_albums"); + } +} diff --git a/3.0/modules/custom_albums/models/custom_album.php b/3.0/modules/custom_albums/models/custom_album.php new file mode 100644 index 00000000..56dc8661 --- /dev/null +++ b/3.0/modules/custom_albums/models/custom_album.php @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/3.0/modules/custom_albums/module.info b/3.0/modules/custom_albums/module.info new file mode 100644 index 00000000..1161c533 --- /dev/null +++ b/3.0/modules/custom_albums/module.info @@ -0,0 +1,3 @@ +name = "Custom Albums" +description = "Lets you set custom thumbnail sizes for albums" +version = 1 From 1c198b4f6a1df111061a420ecd8d4c8f3c022def Mon Sep 17 00:00:00 2001 From: colings Date: Sun, 2 Jan 2011 23:47:30 +0800 Subject: [PATCH 208/300] Maybe working? --- .../helpers/custom_albums_graphics.php | 30 +++++++++++-------- .../helpers/custom_albums_installer.php | 4 --- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php index 5fa6cd49..b6d6702a 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php @@ -37,21 +37,25 @@ class custom_albums_graphics_Core { $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $options["parent_id"])->find(); -/* - $dims = getimagesize($input_file); - if (max($dims[0], $dims[1]) < min($options["width"], $options["height"])) { - // Image would get upscaled; do nothing - copy($input_file, $output_file); - } else { - $image = Image::factory($input_file) - ->resize($options["width"], $options["height"], $options["master"]) - ->quality(module::get_var("gallery", "image_quality")); - if (graphics::can("sharpen")) { - $image->sharpen(module::get_var("gallery", "image_sharpen")); + // If this album has custom data, build the thumbnail at the specified size + if ($albumCustom->loaded()) { + $thumb_size = $albumCustom->thumb_size; + + $dims = getimagesize($input_file); + if (max($dims[0], $dims[1]) < $thumb_size) { + // Image would get upscaled; do nothing + copy($input_file, $output_file); + } else { + $image = Image::factory($input_file) + ->resize($thumb_size, $thumb_size, $options["master"]) + ->quality(module::get_var("gallery", "image_quality")); + if (graphics::can("sharpen")) { + $image->sharpen(module::get_var("gallery", "image_sharpen")); + } + $image->save($output_file); } - $image->save($output_file); } -*/ + module::event("graphics_resize_completed", $input_file, $output_file, $options); } } diff --git a/3.0/modules/custom_albums/helpers/custom_albums_installer.php b/3.0/modules/custom_albums/helpers/custom_albums_installer.php index 477b70e0..319912e9 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_installer.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_installer.php @@ -24,10 +24,6 @@ class custom_albums_installer { "gallery", "thumb", "custom_albums::resize", array("width" => 0, "height" => 0, "master" => Image::AUTO), 200); - graphics::add_rule( - "gallery", "resize", "custom_albums::resize", - array("width" => 0, "height" => 0, "master" => Image::AUTO), - 200); // Create a table to store custom album info in. $db = Database::instance(); From 01a22242d3fb946002780a25719f778f61ebf078 Mon Sep 17 00:00:00 2001 From: colings Date: Mon, 3 Jan 2011 12:19:07 +0800 Subject: [PATCH 209/300] Works, need to fix dirtying on custom size change --- .../helpers/custom_albums_event.php | 23 ++++++---- .../helpers/custom_albums_graphics.php | 41 ++++-------------- .../helpers/custom_albums_installer.php | 42 ++++++++++++++++--- 3 files changed, 58 insertions(+), 48 deletions(-) diff --git a/3.0/modules/custom_albums/helpers/custom_albums_event.php b/3.0/modules/custom_albums/helpers/custom_albums_event.php index b8dbe5aa..3564d410 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_event.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_event.php @@ -18,18 +18,18 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class custom_albums_event_Core { - static function item_edit_form($item, $form) { - if ($item->is_album()) { - $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); + static function item_edit_form($item, $form) { + if ($item->is_album()) { + $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $item->id)->find(); - $thumbdata = $form->edit_item->group("custom_album")->label("Custom Album"); + $thumbdata = $form->edit_item->group("custom_album")->label("Custom Album"); - if ($albumCustom->loaded()) { - $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)"))->value($albumCustom->thumb_size); - } else { - $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)")); - } + if ($albumCustom->loaded()) { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)"))->value($albumCustom->thumb_size); + } else { + $thumbdata->input("thumbsize")->label(t("Thumbnail size (in pixels)")); } + } } static function item_edit_form_completed($item, $form) { @@ -61,4 +61,9 @@ class custom_albums_event_Core { } } } + + static function theme_edit_form_completed($form) { + // Update our resize rules, in case the thumbnail or resize size has changed + custom_albums_installer::update_rules(); + } } diff --git a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php index b6d6702a..833e39be 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_graphics.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_graphics.php @@ -18,44 +18,19 @@ * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ class custom_albums_graphics_Core { - /** - * Resize an image. Valid options are width, height and master. Master is one of the Image - * master dimension constants. - * - * @param string $input_file - * @param string $output_file - * @param array $options - */ - static function resize($input_file, $output_file, $options) { - graphics::init_toolkit(); - - module::event("graphics_resize", $input_file, $output_file, $options); - - if (@filesize($input_file) == 0) { - throw new Exception("@todo EMPTY_INPUT_FILE"); - } - + static function build_thumb($input_file, $output_file, $options) { $albumCustom = ORM::factory("custom_album")->where("album_id", "=", $options["parent_id"])->find(); // If this album has custom data, build the thumbnail at the specified size if ($albumCustom->loaded()) { - $thumb_size = $albumCustom->thumb_size; - - $dims = getimagesize($input_file); - if (max($dims[0], $dims[1]) < $thumb_size) { - // Image would get upscaled; do nothing - copy($input_file, $output_file); - } else { - $image = Image::factory($input_file) - ->resize($thumb_size, $thumb_size, $options["master"]) - ->quality(module::get_var("gallery", "image_quality")); - if (graphics::can("sharpen")) { - $image->sharpen(module::get_var("gallery", "image_sharpen")); - } - $image->save($output_file); - } + $options["width"] = $albumCustom->thumb_size; + $options["height"] = $albumCustom->thumb_size; } - module::event("graphics_resize_completed", $input_file, $output_file, $options); + gallery_graphics::resize($input_file, $output_file, $options); + } + + static function build_resize($input_file, $output_file, $options) { + gallery_graphics::resize($input_file, $output_file, $options); } } diff --git a/3.0/modules/custom_albums/helpers/custom_albums_installer.php b/3.0/modules/custom_albums/helpers/custom_albums_installer.php index 319912e9..b019214e 100644 --- a/3.0/modules/custom_albums/helpers/custom_albums_installer.php +++ b/3.0/modules/custom_albums/helpers/custom_albums_installer.php @@ -19,12 +19,6 @@ */ class custom_albums_installer { static function install() { - // Add rules for generating our thumbnails and resizes - graphics::add_rule( - "gallery", "thumb", "custom_albums::resize", - array("width" => 0, "height" => 0, "master" => Image::AUTO), - 200); - // Create a table to store custom album info in. $db = Database::instance(); @@ -38,6 +32,8 @@ class custom_albums_installer { ) DEFAULT CHARSET=utf8;" ); + custom_albums_installer::update_rules(); + module::set_version("custom_albums", 1); } @@ -47,4 +43,38 @@ class custom_albums_installer { $db->query("DROP TABLE IF EXISTS {custom_albums};"); module::delete("custom_albums"); } + + static function update_rules() { + // Make sure our thumb size matches the gallery one + $thumb_size = module::get_var("gallery", "thumb_size"); + if ($thumb_size != module::get_var("custom_albums", "thumb_size")) { + // Remove and readd our rule with the latest thumb size + graphics::remove_rule("custom_albums", "thumb", "custom_albums_graphics::build_thumb"); + graphics::add_rule( + "custom_albums", "thumb", "custom_albums_graphics::build_thumb", + array("width" => $thumb_size, "height" => $thumb_size, "master" => Image::AUTO), + 101); + + // Deactivate the gallery thumbnail generation, we'll handle it now + graphics::deactivate_rules("gallery"); + + module::set_var("custom_albums", "thumb_size", $thumb_size); + } + + // Make sure our resize size matches the gallery one + $resize_size = module::get_var("gallery", "resize_size"); + if ($resize_size != module::get_var("custom_albums", "resize_size")) { + // Remove and readd our rule with the latest resize size + graphics::remove_rule("custom_albums", "resize", "custom_albums_graphics::build_resize"); + graphics::add_rule( + "custom_albums", "resize", "custom_albums_graphics::build_resize", + array("width" => $resize_size, "height" => $resize_size, "master" => Image::AUTO), + 101); + + // Deactivate the gallery resize, we'll handle it now + graphics::deactivate_rules("gallery"); + + module::set_var("custom_albums", "resize_size", $resize_size); + } + } } From 7ed701b4714f99d7cdb953eef02d206f691264b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Sawicz?= Date: Tue, 4 Jan 2011 00:14:13 +0100 Subject: [PATCH 210/300] [ldap] Use cn when displayName is empty When the optional 'displayName' attribute is empty, use 'cn', which is mandatory and never empty. This prevents empty names being displayed. --- 3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php b/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php index d4e096ff..64b11573 100644 --- a/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php +++ b/3.0/modules/ldap/libraries/drivers/IdentityProvider/Ldap.php @@ -232,7 +232,10 @@ class Ldap_User implements User_Definition { } public function display_name() { - return $this->ldap_entry["displayname"][0]; + if (!empty($this->ldap_entry["displayname"][0])) { + return $this->ldap_entry["displayname"][0]; + } + return $this->ldap_entry["cn"][0]; } public function __get($key) { From e24997eda11f86cdd47b971fde7be80e7c3df3b2 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Tue, 4 Jan 2011 15:57:31 -0700 Subject: [PATCH 211/300] Add bit.ly link to refactored info block via info_block_metadata event hook. --- 3.1/modules/bitly/helpers/bitly_event.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 9a55fe95..f84b234a 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -51,4 +51,17 @@ class bitly_event_Core { ->css_class("g-bitly-shorten ui-icon-link")); } } + + static function info_block_metadata($block, $item_id) { + $link = ORM::factory("bitly_link")->where("item_id", "=", $item_id)->find(); + if ($link->loaded()) { + $info = $block->content->metadata; + $info["bitly_url"] = array( + "label" => t("bit.ly url:"), + "value" => bitly::url($link->hash) + ); + $block->content->metadata = $info; + } + } + } From fa79b1b447cae2d365e1bb44c9e74003d5fb7fb0 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Tue, 4 Jan 2011 19:27:22 -0700 Subject: [PATCH 212/300] Item ID is no longer required by info_block_metadata event, the entire item object is. --- 3.1/modules/bitly/helpers/bitly_event.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index f84b234a..781f33f6 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -52,8 +52,8 @@ class bitly_event_Core { } } - static function info_block_metadata($block, $item_id) { - $link = ORM::factory("bitly_link")->where("item_id", "=", $item_id)->find(); + static function info_block_metadata($block, $item) { + $link = ORM::factory("bitly_link")->where("item_id", "=", $item->id)->find(); if ($link->loaded()) { $info = $block->content->metadata; $info["bitly_url"] = array( From ad0bba3ac3facb55dd7ed7035cac920441eafe90 Mon Sep 17 00:00:00 2001 From: brentil Date: Tue, 4 Jan 2011 22:21:23 -0500 Subject: [PATCH 213/300] MU 5 - Fully supports 3.x versions along with pulling version information from multiple sources (Core, Contrib, & GalleryModules). Code cleanup and debug removal. --- .../controllers/admin_moduleupdates.php | 266 ++++++++++------- .../helpers/moduleupdates_event.php | 2 +- .../helpers/moduleupdates_installer.php | 6 +- 3.0/modules/moduleupdates/module.info | 2 +- .../views/admin_moduleupdates.html.php | 35 ++- .../controllers/admin_moduleupdates.php | 271 +++++++++++------- .../helpers/moduleupdates_installer.php | 8 +- 3.1/modules/moduleupdates/module.info | 2 +- .../views/admin_moduleupdates.html.php | 33 ++- 9 files changed, 382 insertions(+), 243 deletions(-) diff --git a/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php b/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php index b1274111..926d8196 100755 --- a/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php +++ b/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php @@ -16,7 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ - class Admin_Moduleupdates_Controller extends Admin_Controller { /** @@ -44,15 +43,10 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { $view->page_title = t("Gallery 3 :: Manage Module Updates"); $view->content = new View("admin_moduleupdates.html"); - $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") { @@ -78,7 +72,6 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { } } catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
                '; } //Check the ability to access the Google $Google = null; @@ -91,71 +84,79 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { } } catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
                '; } + $update_count = 0; + if($refreshCache == true){ foreach (module::available() as $this_module_name => $module_info) { + + $font_color_local = "black"; + $core_version = ''; + $core_server = ''; + $core_dlink = ''; + $font_color_core = "black"; + $contrib_version = ''; + $contrib_server = ''; + $contrib_dlink = ''; + $font_color_contrib = "black"; + $gh_version = ''; + $gh_server = ''; + $gh_dlink = ''; + $font_color_gh = "black"; - //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_local = $this->get_local_module_version_color ($module_info->version, $module_info->code_version); + list ($core_version, $core_server) = $this->get_remote_module_version($this_module_name, "CORE"); + $font_color_core = $this->get_module_version_color ($module_info->version, $module_info->code_version, $core_version); + list ($contrib_version, $contrib_server) = $this->get_remote_module_version($this_module_name, "CONTRIB"); + $font_color_contrib = $this->get_module_version_color ($module_info->version, $module_info->code_version, $contrib_version); + list ($gh_version, $gh_server) = $this->get_remote_module_version($this_module_name, "GH"); + $font_color_gh = $this->get_module_version_color ($module_info->version, $module_info->code_version, $gh_version); - $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"; + if($font_color_core == "red" or $font_color_contrib == "red" or $font_color_gh == "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.""; + if (is_numeric($core_version)) { + if($core_version > $module_info->version) { + //https://github.com/gallery/gallery3/tree/master/modules/recaptcha + //"http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name + $core_dlink = "http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name; + } + } + + if (is_numeric($contrib_version)) { + if($contrib_version > $module_info->version) { + //https://github.com/gallery/gallery3-contrib/tree/master/3.0/modules/moduleupdates + //"http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name + $contrib_dlink = "http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name; + } + } + + if (is_numeric($gh_version)) { + if($gh_version > $module_info->version) { + $gh_dlink = "http://www.gallerymodules.com/update/".$this_module_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); + "core_version" => $core_version, "core_server" => $core_server, "font_color_core" => $font_color_core, + "contrib_version" => $contrib_version, "contrib_server" => $contrib_server, "font_color_contrib" => $font_color_contrib, + "gh_version" => $gh_version, "gh_server" => $gh_server, "font_color_gh" => $font_color_gh, + "font_color_local" => $font_color_local, "core_dlink" => $core_dlink, "contrib_dlink" => $contrib_dlink, + "gh_dlink" => $gh_dlink); } //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); @@ -174,6 +175,52 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { } + /** + * + **/ + private function get_module_version_color ($version, $code_version, $remote_version) { + + $font_color = "black"; + + //BLACK - no module version detected + if ($remote_version == "") { + $font_color = "black"; + //BLUE - DNE: Does Not Exist, this module was not found + } else if ($remote_version == "DNE") { + $font_color = "blue"; + //GREEN - Your version is newer than the GitHub + } else if ($remote_version < $code_version or ($version != '' + and $remote_version < $version)) { + $font_color = "green"; + //RED - Your version is older than the GitHub + } else if ($remote_version > $code_version or ($version != '' + and $remote_version > $version)) { + $font_color = "red"; + } + + return $font_color; + } + + + /** + * + **/ + private function get_local_module_version_color ($version, $code_version) { + + $font_color = "black"; + + //PINK - Your installed version is newer than file version + if ($version != '' and $code_version < $version) { + $font_color = "pink"; + //ORANGE - Your file version is newer than the installed version + } else if ($version != '' and $code_version > $version) { + $font_color = "orange"; + } + + return $font_color; + } + + /** * Parses the known GitHub repositories for new versions of modules. * @@ -183,71 +230,82 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { * * http://github.com/gallery/gallery3 * http://github.com/gallery/gallery3-contrib + * http://www.gallerymodules.com * * @author brentil - * @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. + * @param String - The folder name of the module to search for on the remote GitHub server + * @param String - The remote server to check against + * @return Array - An array with the remote module version and the server it was found on. */ - private function get_remote_module_version ($module_name, $devDebug) { + private function get_remote_module_version ($module_name, $server_location) { - $version = 'DNE'; + $version = ''; $server = ''; $file = null; - //For development debug only - if ($devDebug == true){ - if ($file == null) { - try { - $file = fopen ("http://github.com/brentil/gallery3-contrib/raw/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$module_name."/module.info", "r"); - if ($file != null) { - $server = '(brentil)'; + switch ($server_location) { + case "CONTRIB": + //Check the Gallery3 Community Contributions GitHub + if ($file == null) { + try { + $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$module_name."/module.info", "r"); + if ($file != null) { + $server = '(GCC)'; + } + } + catch (Exception $e) { + } } - } - 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 = '(G)'; - } - } - catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
                '; - } + break; + case "CORE": + //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 = '(G)'; + } + } + catch (Exception $e) { + } + } + break; + case "GH": + //Check GalleryModules.com + if ($file == null) { + try { + $file = fopen ("http://www.gallerymodules.com/m/".$module_name, "r"); + if ($file != null) { + $server = '(GH)'; + } + } + catch (Exception $e) { + } + } + break; } - - //Check the Gallery3 Community Contributions GitHub - if ($file == null) { - try { - $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$module_name."/module.info", "r"); - if ($file != null) { - $server = '(GCC)'; - } - } - catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
                '; - } - } - - if ($file != null) { + + if ($file != null) { while (!feof ($file)) { $line = fgets ($file, 1024); - - //Regular expression to find & gather the version number in the remote module.info file - if (preg_match ("@version = (.*)@i", $line, $out)) { - $version = $out[1]; - break; - } + if ($server_location == "GH"){ + //GH stores only the version info + if($line == "Not entered" or $line == "See git") { + $line = ""; + } + $version = $line; + break; + } else { + //Regular expression to find & gather the version number in the remote module.info file + if (preg_match ("@version = (.*)@i", $line, $out)) { + $version = $out[1]; + break; + } + } } fclose ($file); } - return array ($version, $server); - } -} + return array ($version, $server); + } +} \ No newline at end of file diff --git a/3.0/modules/moduleupdates/helpers/moduleupdates_event.php b/3.0/modules/moduleupdates/helpers/moduleupdates_event.php index 8dbed46c..dd6efc9c 100644 --- a/3.0/modules/moduleupdates/helpers/moduleupdates_event.php +++ b/3.0/modules/moduleupdates/helpers/moduleupdates_event.php @@ -27,4 +27,4 @@ class moduleupdates_event_Core { ->label(t("Module Updates")) ->url(url::site("admin/moduleupdates"))); } -} +} \ No newline at end of file diff --git a/3.0/modules/moduleupdates/helpers/moduleupdates_installer.php b/3.0/modules/moduleupdates/helpers/moduleupdates_installer.php index 39d1dcc0..911f6b81 100644 --- a/3.0/modules/moduleupdates/helpers/moduleupdates_installer.php +++ b/3.0/modules/moduleupdates/helpers/moduleupdates_installer.php @@ -23,8 +23,8 @@ class moduleupdates_installer { static function install() { $version = module::get_version("moduleupdates"); - if ($version == 0) { - module::set_version("moduleupdates", 4); + if ($version < 1) { + module::set_version("moduleupdates", 5); //Remove the ModuleUpdates cache entry 'JIC' Cache::instance()->delete("ModuleUpdates"); //create the blank ModuleUpdates cache entry with an expiration of 0 days @@ -34,7 +34,7 @@ class moduleupdates_installer { } static function upgrade($version) { - module::set_version("moduleupdates", 4); + module::set_version("moduleupdates", 5); //Remove the ModuleUpdates cache entry 'JIC' Cache::instance()->delete("ModuleUpdates"); //Empty the ModuleUpdates cache entry so our new version starts from scratch diff --git a/3.0/modules/moduleupdates/module.info b/3.0/modules/moduleupdates/module.info index 722abecc..34868eaf 100755 --- a/3.0/modules/moduleupdates/module.info +++ b/3.0/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 = 4 \ No newline at end of file +version = 5 \ No newline at end of file diff --git a/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php index 3d7d5453..d1e0aa3a 100644 --- a/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -2,7 +2,7 @@
                -

                +

                @@ -16,7 +16,6 @@
              • 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,24 +24,36 @@
                -
              • +
              - - - - + + + + + + "> - - - - + + + + + + + + + + + + + +
              [File/Installed]") ?> Installed") ?>
              "; ?> "; ?> "; ?> "; ?> "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?>
              Installed") ?>
              -
            +
          \ No newline at end of file diff --git a/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php b/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php index ee74c90d..926d8196 100755 --- a/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php +++ b/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php @@ -16,7 +16,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ - class Admin_Moduleupdates_Controller extends Admin_Controller { /** @@ -44,15 +43,10 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { $view->page_title = t("Gallery 3 :: Manage Module Updates"); $view->content = new View("admin_moduleupdates.html"); - $devDebug = false; - $refreshCache = false; + $refreshCache = false; $cache = unserialize(Cache::instance()->get("moduleupdates_cache")); - $cache_updates = unserialize(Cache::instance()->get("moduleupdates_cache_updates")); - - //--------------------------------------------------------------------------------------------- - //echo 'Message 01: ' .$cache_updates . '
          '; - //--------------------------------------------------------------------------------------------- + $cache_updates = unserialize(Cache::instance()->get("moduleupdates_cache_updates")); //if someone pressed the button to refresh now if (request::method() == "post") { @@ -78,7 +72,6 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { } } catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
          '; } //Check the ability to access the Google $Google = null; @@ -91,71 +84,79 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { } } catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
          '; } + $update_count = 0; + if($refreshCache == true){ foreach (module::available() as $this_module_name => $module_info) { + + $font_color_local = "black"; + $core_version = ''; + $core_server = ''; + $core_dlink = ''; + $font_color_core = "black"; + $contrib_version = ''; + $contrib_server = ''; + $contrib_dlink = ''; + $font_color_contrib = "black"; + $gh_version = ''; + $gh_server = ''; + $gh_dlink = ''; + $font_color_gh = "black"; - //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_local = $this->get_local_module_version_color ($module_info->version, $module_info->code_version); + list ($core_version, $core_server) = $this->get_remote_module_version($this_module_name, "CORE"); + $font_color_core = $this->get_module_version_color ($module_info->version, $module_info->code_version, $core_version); + list ($contrib_version, $contrib_server) = $this->get_remote_module_version($this_module_name, "CONTRIB"); + $font_color_contrib = $this->get_module_version_color ($module_info->version, $module_info->code_version, $contrib_version); + list ($gh_version, $gh_server) = $this->get_remote_module_version($this_module_name, "GH"); + $font_color_gh = $this->get_module_version_color ($module_info->version, $module_info->code_version, $gh_version); - $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"; + if($font_color_core == "red" or $font_color_contrib == "red" or $font_color_gh == "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.""; + if (is_numeric($core_version)) { + if($core_version > $module_info->version) { + //https://github.com/gallery/gallery3/tree/master/modules/recaptcha + //"http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name + $core_dlink = "http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name; + } + } + + if (is_numeric($contrib_version)) { + if($contrib_version > $module_info->version) { + //https://github.com/gallery/gallery3-contrib/tree/master/3.0/modules/moduleupdates + //"http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name + $contrib_dlink = "http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name; + } + } + + if (is_numeric($gh_version)) { + if($gh_version > $module_info->version) { + $gh_dlink = "http://www.gallerymodules.com/update/".$this_module_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); + "core_version" => $core_version, "core_server" => $core_server, "font_color_core" => $font_color_core, + "contrib_version" => $contrib_version, "contrib_server" => $contrib_server, "font_color_contrib" => $font_color_contrib, + "gh_version" => $gh_version, "gh_server" => $gh_server, "font_color_gh" => $font_color_gh, + "font_color_local" => $font_color_local, "core_dlink" => $core_dlink, "contrib_dlink" => $contrib_dlink, + "gh_dlink" => $gh_dlink); } //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); @@ -167,12 +168,59 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { $view->content->csrf = access::csrf_token(); $view->content->Google = $Google; $view->content->GitHub = $GitHub; + $view->content->Gallery_Version = substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")); print $view; } + /** + * + **/ + private function get_module_version_color ($version, $code_version, $remote_version) { + + $font_color = "black"; + + //BLACK - no module version detected + if ($remote_version == "") { + $font_color = "black"; + //BLUE - DNE: Does Not Exist, this module was not found + } else if ($remote_version == "DNE") { + $font_color = "blue"; + //GREEN - Your version is newer than the GitHub + } else if ($remote_version < $code_version or ($version != '' + and $remote_version < $version)) { + $font_color = "green"; + //RED - Your version is older than the GitHub + } else if ($remote_version > $code_version or ($version != '' + and $remote_version > $version)) { + $font_color = "red"; + } + + return $font_color; + } + + + /** + * + **/ + private function get_local_module_version_color ($version, $code_version) { + + $font_color = "black"; + + //PINK - Your installed version is newer than file version + if ($version != '' and $code_version < $version) { + $font_color = "pink"; + //ORANGE - Your file version is newer than the installed version + } else if ($version != '' and $code_version > $version) { + $font_color = "orange"; + } + + return $font_color; + } + + /** * Parses the known GitHub repositories for new versions of modules. * @@ -182,71 +230,82 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { * * http://github.com/gallery/gallery3 * http://github.com/gallery/gallery3-contrib + * http://www.gallerymodules.com * * @author brentil - * @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. + * @param String - The folder name of the module to search for on the remote GitHub server + * @param String - The remote server to check against + * @return Array - An array with the remote module version and the server it was found on. */ - private function get_remote_module_version ($module_name, $devDebug) { + private function get_remote_module_version ($module_name, $server_location) { - $version = 'DNE'; + $version = ''; $server = ''; $file = null; - //For development debug only - if ($devDebug == true){ - if ($file == null) { - try { - $file = fopen ("http://github.com/brentil/gallery3-contrib/raw/master/3.1/modules/".$module_name."/module.info", "r"); - if ($file != null) { - $server = '(brentil)'; + switch ($server_location) { + case "CONTRIB": + //Check the Gallery3 Community Contributions GitHub + if ($file == null) { + try { + $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$module_name."/module.info", "r"); + if ($file != null) { + $server = '(GCC)'; + } + } + catch (Exception $e) { + } } - } - 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() . '
          '; - } + break; + case "CORE": + //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 = '(G)'; + } + } + catch (Exception $e) { + } + } + break; + case "GH": + //Check GalleryModules.com + if ($file == null) { + try { + $file = fopen ("http://www.gallerymodules.com/m/".$module_name, "r"); + if ($file != null) { + $server = '(GH)'; + } + } + catch (Exception $e) { + } + } + break; } - - //Check the Gallery3 Community Contributions GitHub - if ($file == null) { - try { - $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/3.1/modules/".$module_name."/module.info", "r"); - if ($file != null) { - $server = '(G3CC)'; - } - } - catch (Exception $e) { - //echo 'Message: ' .$e->getMessage() . '
          '; - } - } - - if ($file != null) { + + if ($file != null) { while (!feof ($file)) { $line = fgets ($file, 1024); - - //Regular expression to find & gather the version number in the remote module.info file - if (preg_match ("@version = (.*)@i", $line, $out)) { - $version = $out[1]; - break; - } + if ($server_location == "GH"){ + //GH stores only the version info + if($line == "Not entered" or $line == "See git") { + $line = ""; + } + $version = $line; + break; + } else { + //Regular expression to find & gather the version number in the remote module.info file + if (preg_match ("@version = (.*)@i", $line, $out)) { + $version = $out[1]; + break; + } + } } fclose ($file); } - return array ($version, $server); - } -} + return array ($version, $server); + } +} \ No newline at end of file diff --git a/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php b/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php index dd0dddb8..911f6b81 100644 --- a/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php +++ b/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php @@ -23,8 +23,8 @@ class moduleupdates_installer { static function install() { $version = module::get_version("moduleupdates"); - if ($version == 0) { - module::set_version("moduleupdates", 2); + if ($version < 1) { + module::set_version("moduleupdates", 5); //Remove the ModuleUpdates cache entry 'JIC' Cache::instance()->delete("ModuleUpdates"); //create the blank ModuleUpdates cache entry with an expiration of 0 days @@ -34,7 +34,7 @@ class moduleupdates_installer { } static function upgrade($version) { - module::set_version("moduleupdates", 2); + module::set_version("moduleupdates", 5); //Remove the ModuleUpdates cache entry 'JIC' Cache::instance()->delete("ModuleUpdates"); //Empty the ModuleUpdates cache entry so our new version starts from scratch @@ -48,4 +48,4 @@ class moduleupdates_installer { Cache::instance()->delete("ModuleUpdates"); module::delete("moduleupdates"); } -} +} \ No newline at end of file diff --git a/3.1/modules/moduleupdates/module.info b/3.1/modules/moduleupdates/module.info index cf43770b..34868eaf 100755 --- a/3.1/modules/moduleupdates/module.info +++ b/3.1/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 = 3 +version = 5 \ No newline at end of file diff --git a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php index 3fdc06a7..d1e0aa3a 100644 --- a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -2,7 +2,7 @@
          -

          +

          @@ -16,7 +16,6 @@
        • 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,24 +24,36 @@
          -
        • +
        - - - - + + + + + + "> - - - - + + + + + + + + + + + + + +
        [File/Installed]") ?> Installed") ?>
        "; ?> "; ?> "; ?> "; ?> "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?>
        Installed") ?>
      \ No newline at end of file From 14cf53f70602146132a82ead0aa687b780717b0e Mon Sep 17 00:00:00 2001 From: brentil Date: Wed, 5 Jan 2011 20:54:37 -0500 Subject: [PATCH 214/300] Fix for GalleryModules 3.1 version paths and clarified verbiage. --- .../controllers/admin_moduleupdates.php | 25 ++++++++----- .../views/admin_moduleupdates.html.php | 36 +++++++++---------- .../controllers/admin_moduleupdates.php | 25 ++++++++----- .../views/admin_moduleupdates.html.php | 36 +++++++++---------- 4 files changed, 70 insertions(+), 52 deletions(-) diff --git a/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php b/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php index 926d8196..8c06c351 100755 --- a/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php +++ b/3.0/modules/moduleupdates/controllers/admin_moduleupdates.php @@ -123,23 +123,26 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { if (is_numeric($core_version)) { if($core_version > $module_info->version) { - //https://github.com/gallery/gallery3/tree/master/modules/recaptcha - //"http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name $core_dlink = "http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name; } } if (is_numeric($contrib_version)) { if($contrib_version > $module_info->version) { - //https://github.com/gallery/gallery3-contrib/tree/master/3.0/modules/moduleupdates - //"http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name - $contrib_dlink = "http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name; + $contrib_dlink = "http://github.com/gallery/gallery3-contrib/tree/master/". + substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name; } } if (is_numeric($gh_version)) { if($gh_version > $module_info->version) { - $gh_dlink = "http://www.gallerymodules.com/update/".$this_module_name; + $this_gm_repo = str_replace(".","",substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," "))); + if($this_gm_repo == "30"){ + $gh_dlink = "http://www.gallerymodules.com/update/".$this_module_name; + } else { + $gh_dlink = "http://www.gallerymodules.com/update".this_gm_repo."/".$this_module_name; + } + } } @@ -248,7 +251,8 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { //Check the Gallery3 Community Contributions GitHub if ($file == null) { try { - $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$module_name."/module.info", "r"); + $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". + substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," "))."/modules/".$module_name."/module.info", "r"); if ($file != null) { $server = '(GCC)'; } @@ -274,7 +278,12 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { //Check GalleryModules.com if ($file == null) { try { - $file = fopen ("http://www.gallerymodules.com/m/".$module_name, "r"); + $this_gm_repo = str_replace(".","",substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," "))); + if($this_gm_repo == "30"){ + $file = fopen ("http://www.gallerymodules.com/m/".$module_name, "r"); + } else { + $file = fopen ("http://www.gallerymodules.com/".this_gm_repo."m/".$module_name, "r"); + } if ($file != null) { $server = '(GH)'; } diff --git a/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php index d1e0aa3a..a5dc4a0b 100644 --- a/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -3,7 +3,7 @@

      - +
      @@ -24,35 +24,35 @@
        -
      • +
      - - - - - - + + + + + + "> - - - - + + + + - - - - - - + + + + + +
      Installed") ?>
      Installed") ?>
      "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?> "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?>
      Installed") ?>
      Installed") ?>
      diff --git a/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php b/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php index 926d8196..8c06c351 100755 --- a/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php +++ b/3.1/modules/moduleupdates/controllers/admin_moduleupdates.php @@ -123,23 +123,26 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { if (is_numeric($core_version)) { if($core_version > $module_info->version) { - //https://github.com/gallery/gallery3/tree/master/modules/recaptcha - //"http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name $core_dlink = "http://github.com/gallery/gallery3/tree/master/modules/".$this_module_name; } } if (is_numeric($contrib_version)) { if($contrib_version > $module_info->version) { - //https://github.com/gallery/gallery3-contrib/tree/master/3.0/modules/moduleupdates - //"http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name - $contrib_dlink = "http://github.com/gallery/gallery3-contrib/tree/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name; + $contrib_dlink = "http://github.com/gallery/gallery3-contrib/tree/master/". + substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$this_module_name; } } if (is_numeric($gh_version)) { if($gh_version > $module_info->version) { - $gh_dlink = "http://www.gallerymodules.com/update/".$this_module_name; + $this_gm_repo = str_replace(".","",substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," "))); + if($this_gm_repo == "30"){ + $gh_dlink = "http://www.gallerymodules.com/update/".$this_module_name; + } else { + $gh_dlink = "http://www.gallerymodules.com/update".this_gm_repo."/".$this_module_name; + } + } } @@ -248,7 +251,8 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { //Check the Gallery3 Community Contributions GitHub if ($file == null) { try { - $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," ")) ."/modules/".$module_name."/module.info", "r"); + $file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/". + substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," "))."/modules/".$module_name."/module.info", "r"); if ($file != null) { $server = '(GCC)'; } @@ -274,7 +278,12 @@ class Admin_Moduleupdates_Controller extends Admin_Controller { //Check GalleryModules.com if ($file == null) { try { - $file = fopen ("http://www.gallerymodules.com/m/".$module_name, "r"); + $this_gm_repo = str_replace(".","",substr_replace(gallery::VERSION,"",strpos(gallery::VERSION," "))); + if($this_gm_repo == "30"){ + $file = fopen ("http://www.gallerymodules.com/m/".$module_name, "r"); + } else { + $file = fopen ("http://www.gallerymodules.com/".this_gm_repo."m/".$module_name, "r"); + } if ($file != null) { $server = '(GH)'; } diff --git a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php index d1e0aa3a..a5dc4a0b 100644 --- a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -3,7 +3,7 @@

      - +
      @@ -24,35 +24,35 @@
        -
      • +
      - - - - - - + + + + + + "> - - - - + + + + - - - - - - + + + + + +
      Installed") ?>
      Installed") ?>
      "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?> "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?>
      Installed") ?>
      Installed") ?>
      From b2398036ec4af826fec23c6a6e6e9c3b314e026a Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Thu, 6 Jan 2011 21:06:40 -0700 Subject: [PATCH 215/300] Changed info_block_metadata event to info_block_get_metadata per 06d94065ce0919f34151. --- 3.1/modules/bitly/helpers/bitly_event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 781f33f6..68670e4d 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -52,7 +52,7 @@ class bitly_event_Core { } } - static function info_block_metadata($block, $item) { + static function info_block_get_metadata($block, $item) { $link = ORM::factory("bitly_link")->where("item_id", "=", $item->id)->find(); if ($link->loaded()) { $info = $block->content->metadata; From ba0e78e269aa97beeed0392074530398771892b8 Mon Sep 17 00:00:00 2001 From: dmolavi Date: Fri, 7 Jan 2011 15:36:16 -0500 Subject: [PATCH 216/300] Fix for CSS display issues. Closes 1583 --- 3.0/modules/ecard/helpers/ecard_block.php | 2 +- 3.0/modules/ecard/views/ecard_block.html.php | 4 ++-- 3.1/modules/ecard/helpers/ecard_block.php | 2 +- 3.1/modules/ecard/views/ecard_block.html.php | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/3.0/modules/ecard/helpers/ecard_block.php b/3.0/modules/ecard/helpers/ecard_block.php index 051c55c6..89e818d7 100644 --- a/3.0/modules/ecard/helpers/ecard_block.php +++ b/3.0/modules/ecard/helpers/ecard_block.php @@ -28,7 +28,7 @@ class ecard_block_Core { case "ecard": if ($theme->item() && $theme->item()->is_photo() && module::get_var("ecard", "location") == "sidebar") { $block = new Block(); - $block->css_id = "g-send-ecard"; + $block->css_id = "g-sendecard"; $block->title = t("eCard"); $block->content = new View("ecard_block.html"); } diff --git a/3.0/modules/ecard/views/ecard_block.html.php b/3.0/modules/ecard/views/ecard_block.html.php index 3f307a1d..d8aa4e4e 100644 --- a/3.0/modules/ecard/views/ecard_block.html.php +++ b/3.0/modules/ecard/views/ecard_block.html.php @@ -1,6 +1,6 @@ -id}") ?>" id="g-send-ecard" +id}") ?>" class="g-dialog-link g-button ui-state-default ui-corner-all"> - + diff --git a/3.1/modules/ecard/helpers/ecard_block.php b/3.1/modules/ecard/helpers/ecard_block.php index 051c55c6..89e818d7 100644 --- a/3.1/modules/ecard/helpers/ecard_block.php +++ b/3.1/modules/ecard/helpers/ecard_block.php @@ -28,7 +28,7 @@ class ecard_block_Core { case "ecard": if ($theme->item() && $theme->item()->is_photo() && module::get_var("ecard", "location") == "sidebar") { $block = new Block(); - $block->css_id = "g-send-ecard"; + $block->css_id = "g-sendecard"; $block->title = t("eCard"); $block->content = new View("ecard_block.html"); } diff --git a/3.1/modules/ecard/views/ecard_block.html.php b/3.1/modules/ecard/views/ecard_block.html.php index 3f307a1d..d8aa4e4e 100644 --- a/3.1/modules/ecard/views/ecard_block.html.php +++ b/3.1/modules/ecard/views/ecard_block.html.php @@ -1,6 +1,6 @@ -id}") ?>" id="g-send-ecard" +id}") ?>" class="g-dialog-link g-button ui-state-default ui-corner-all"> - + From 687b82749564e957d3293f919f6f13487cc76bbb Mon Sep 17 00:00:00 2001 From: Rob Date: Sat, 8 Jan 2011 01:47:48 -0800 Subject: [PATCH 217/300] removed ophan div tag --- 3.0/modules/albumtree/views/albumtree_block_list.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.0/modules/albumtree/views/albumtree_block_list.html.php b/3.0/modules/albumtree/views/albumtree_block_list.html.php index 43db3a24..0e2cdb6d 100644 --- a/3.0/modules/albumtree/views/albumtree_block_list.html.php +++ b/3.0/modules/albumtree/views/albumtree_block_list.html.php @@ -34,5 +34,5 @@
    -
    + From 8035ed40badcd331606e796cbcdd3000f24bafd9 Mon Sep 17 00:00:00 2001 From: Rob Date: Sat, 8 Jan 2011 01:54:54 -0800 Subject: [PATCH 218/300] removed orphan div tag --- 3.1/modules/albumtree/views/albumtree_block_list.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/albumtree/views/albumtree_block_list.html.php b/3.1/modules/albumtree/views/albumtree_block_list.html.php index 43db3a24..0e2cdb6d 100644 --- a/3.1/modules/albumtree/views/albumtree_block_list.html.php +++ b/3.1/modules/albumtree/views/albumtree_block_list.html.php @@ -34,5 +34,5 @@ -
    + From 878ba14230dfc291b26bdf1b26628c0112781698 Mon Sep 17 00:00:00 2001 From: undagiga Date: Sat, 8 Jan 2011 22:32:08 +1100 Subject: [PATCH 219/300] This is my second attempt to update about_this_photo to V3 and to add the new module about_this_album. Hopefully I have pulled all upstream changes this time. --- .../helpers/about_this_album_block.php | 76 +++++++++++++++++++ 3.0/modules/about_this_album/module.info | 3 + .../views/about_this_album.html.php | 68 +++++++++++++++++ .../helpers/about_this_photo_block.php | 2 +- 3.0/modules/about_this_photo/module.info | 2 +- .../views/about_this_photo.html.php | 8 +- .../helpers/about_this_album_block.php | 76 +++++++++++++++++++ 3.1/modules/about_this_album/module.info | 3 + .../views/about_this_album.html.php | 68 +++++++++++++++++ .../helpers/about_this_photo_block.php | 2 +- 3.1/modules/about_this_photo/module.info | 2 +- .../views/about_this_photo.html.php | 8 +- 12 files changed, 306 insertions(+), 12 deletions(-) create mode 100644 3.0/modules/about_this_album/helpers/about_this_album_block.php create mode 100644 3.0/modules/about_this_album/module.info create mode 100644 3.0/modules/about_this_album/views/about_this_album.html.php create mode 100644 3.1/modules/about_this_album/helpers/about_this_album_block.php create mode 100644 3.1/modules/about_this_album/module.info create mode 100644 3.1/modules/about_this_album/views/about_this_album.html.php diff --git a/3.0/modules/about_this_album/helpers/about_this_album_block.php b/3.0/modules/about_this_album/helpers/about_this_album_block.php new file mode 100644 index 00000000..a2a849e5 --- /dev/null +++ b/3.0/modules/about_this_album/helpers/about_this_album_block.php @@ -0,0 +1,76 @@ + t("About This Album")); + } + + static function get($block_id, $theme) { + switch ($block_id) { + case "aboutthisalbum": + $item = $theme->item; + if ((!$item) or (!$theme->item->is_album())) { + return ""; + } + if ($theme->item->is_album()) { + $block = new Block(); + $block->css_id = "g-about-this-album"; + $block->content = new View("about_this_album.html"); + + if ($theme->item()->id == item::root()->id) { + $block->title = t("About this Site"); + $block->content->album_count = ORM::factory("item")->where("type", "=", "album")->where("id", "<>", 1)->count_all(); + $block->content->photo_count = ORM::factory("item")->where("type", "=", "photo")->count_all(); + $block->content->vcount = Database::instance()->query("SELECT SUM({items}.view_count) as c FROM {items} WHERE type=\"photo\"")->current()->c; + } Else { + $block->title = t("About this Album"); + $block->content->album_count = $item->descendants_count(array(array("type", "=", "album"))); + $block->content->photo_count = $item->descendants_count(array(array("type", "=", "photo"))); + // $block->content->vcount= $theme->item()->view_count; + $descds = $item->descendants(); + $descds_view = 0; + foreach ($descds as $descd) { + if ($descd->is_photo()) { + $descds_view += $descd->view_count; + } + } + $block->content->vcount = $descds_view; + if ($item->description) { + $block->content->description = html::clean($item->description); + } + } + + + $all_tags = ORM::factory("tag") + ->join("items_tags", "items_tags.tag_id", "tags.id") + ->join("items", "items.id", "items_tags.item_id", "LEFT") + ->where("items.parent_id", "=", $item->id) + ->order_by("tags.id", "ASC") + ->find_all(); + if (count($all_tags) > 0) { + $block->content->all_tags = $all_tags; + } + } + break; + } + return $block; + } +} diff --git a/3.0/modules/about_this_album/module.info b/3.0/modules/about_this_album/module.info new file mode 100644 index 00000000..8080a24d --- /dev/null +++ b/3.0/modules/about_this_album/module.info @@ -0,0 +1,3 @@ +name = "About this Album" +description = "Show some simple, specific and useful info about a given album" +version = 1 diff --git a/3.0/modules/about_this_album/views/about_this_album.html.php b/3.0/modules/about_this_album/views/about_this_album.html.php new file mode 100644 index 00000000..01dee4f7 --- /dev/null +++ b/3.0/modules/about_this_album/views/about_this_album.html.php @@ -0,0 +1,68 @@ + + + diff --git a/3.0/modules/about_this_photo/helpers/about_this_photo_block.php b/3.0/modules/about_this_photo/helpers/about_this_photo_block.php index 86869844..808d19bd 100644 --- a/3.0/modules/about_this_photo/helpers/about_this_photo_block.php +++ b/3.0/modules/about_this_photo/helpers/about_this_photo_block.php @@ -54,7 +54,7 @@ class about_this_photo_block_Core { $record = ORM::factory("iptc_record")->where("item_id", "=", $theme->item()->id)->find(); if ($record->loaded()) { $record = unserialize($record->data); - $block->content->source = $record["Source"]; + $block->content->name = $record["ObjectName"]; $block->content->caption = $record["Caption"]; } diff --git a/3.0/modules/about_this_photo/module.info b/3.0/modules/about_this_photo/module.info index 632c0f05..e324ae3b 100644 --- a/3.0/modules/about_this_photo/module.info +++ b/3.0/modules/about_this_photo/module.info @@ -1,3 +1,3 @@ name = "About this Photo" description = "Show some simple, specific and useful info about a given photo" -version = 2 +version = 3 diff --git a/3.0/modules/about_this_photo/views/about_this_photo.html.php b/3.0/modules/about_this_photo/views/about_this_photo.html.php index 45db0274..f0ef130a 100644 --- a/3.0/modules/about_this_photo/views/about_this_photo.html.php +++ b/3.0/modules/about_this_photo/views/about_this_photo.html.php @@ -16,14 +16,14 @@ - - + + - +
    -
    +
    diff --git a/3.1/modules/about_this_album/helpers/about_this_album_block.php b/3.1/modules/about_this_album/helpers/about_this_album_block.php new file mode 100644 index 00000000..a2a849e5 --- /dev/null +++ b/3.1/modules/about_this_album/helpers/about_this_album_block.php @@ -0,0 +1,76 @@ + t("About This Album")); + } + + static function get($block_id, $theme) { + switch ($block_id) { + case "aboutthisalbum": + $item = $theme->item; + if ((!$item) or (!$theme->item->is_album())) { + return ""; + } + if ($theme->item->is_album()) { + $block = new Block(); + $block->css_id = "g-about-this-album"; + $block->content = new View("about_this_album.html"); + + if ($theme->item()->id == item::root()->id) { + $block->title = t("About this Site"); + $block->content->album_count = ORM::factory("item")->where("type", "=", "album")->where("id", "<>", 1)->count_all(); + $block->content->photo_count = ORM::factory("item")->where("type", "=", "photo")->count_all(); + $block->content->vcount = Database::instance()->query("SELECT SUM({items}.view_count) as c FROM {items} WHERE type=\"photo\"")->current()->c; + } Else { + $block->title = t("About this Album"); + $block->content->album_count = $item->descendants_count(array(array("type", "=", "album"))); + $block->content->photo_count = $item->descendants_count(array(array("type", "=", "photo"))); + // $block->content->vcount= $theme->item()->view_count; + $descds = $item->descendants(); + $descds_view = 0; + foreach ($descds as $descd) { + if ($descd->is_photo()) { + $descds_view += $descd->view_count; + } + } + $block->content->vcount = $descds_view; + if ($item->description) { + $block->content->description = html::clean($item->description); + } + } + + + $all_tags = ORM::factory("tag") + ->join("items_tags", "items_tags.tag_id", "tags.id") + ->join("items", "items.id", "items_tags.item_id", "LEFT") + ->where("items.parent_id", "=", $item->id) + ->order_by("tags.id", "ASC") + ->find_all(); + if (count($all_tags) > 0) { + $block->content->all_tags = $all_tags; + } + } + break; + } + return $block; + } +} diff --git a/3.1/modules/about_this_album/module.info b/3.1/modules/about_this_album/module.info new file mode 100644 index 00000000..8080a24d --- /dev/null +++ b/3.1/modules/about_this_album/module.info @@ -0,0 +1,3 @@ +name = "About this Album" +description = "Show some simple, specific and useful info about a given album" +version = 1 diff --git a/3.1/modules/about_this_album/views/about_this_album.html.php b/3.1/modules/about_this_album/views/about_this_album.html.php new file mode 100644 index 00000000..01dee4f7 --- /dev/null +++ b/3.1/modules/about_this_album/views/about_this_album.html.php @@ -0,0 +1,68 @@ + + + diff --git a/3.1/modules/about_this_photo/helpers/about_this_photo_block.php b/3.1/modules/about_this_photo/helpers/about_this_photo_block.php index 86869844..808d19bd 100644 --- a/3.1/modules/about_this_photo/helpers/about_this_photo_block.php +++ b/3.1/modules/about_this_photo/helpers/about_this_photo_block.php @@ -54,7 +54,7 @@ class about_this_photo_block_Core { $record = ORM::factory("iptc_record")->where("item_id", "=", $theme->item()->id)->find(); if ($record->loaded()) { $record = unserialize($record->data); - $block->content->source = $record["Source"]; + $block->content->name = $record["ObjectName"]; $block->content->caption = $record["Caption"]; } diff --git a/3.1/modules/about_this_photo/module.info b/3.1/modules/about_this_photo/module.info index 632c0f05..e324ae3b 100644 --- a/3.1/modules/about_this_photo/module.info +++ b/3.1/modules/about_this_photo/module.info @@ -1,3 +1,3 @@ name = "About this Photo" description = "Show some simple, specific and useful info about a given photo" -version = 2 +version = 3 diff --git a/3.1/modules/about_this_photo/views/about_this_photo.html.php b/3.1/modules/about_this_photo/views/about_this_photo.html.php index 45db0274..f0ef130a 100644 --- a/3.1/modules/about_this_photo/views/about_this_photo.html.php +++ b/3.1/modules/about_this_photo/views/about_this_photo.html.php @@ -16,14 +16,14 @@ - - + + - +
    -
    +
    From 7904946b54b9444e4107338f3e746d8daef9f968 Mon Sep 17 00:00:00 2001 From: dmolavi Date: Sat, 8 Jan 2011 20:16:32 -0500 Subject: [PATCH 220/300] Oops, forgot to bump version number. --- 3.0/modules/ecard/helpers/ecard_installer.php | 2 +- 3.0/modules/ecard/module.info | 2 +- 3.1/modules/ecard/helpers/ecard_installer.php | 2 +- 3.1/modules/ecard/module.info | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3.0/modules/ecard/helpers/ecard_installer.php b/3.0/modules/ecard/helpers/ecard_installer.php index d9887094..a9a6934f 100644 --- a/3.0/modules/ecard/helpers/ecard_installer.php +++ b/3.0/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", 4); + module::set_version("ecard", 5); } } diff --git a/3.0/modules/ecard/module.info b/3.0/modules/ecard/module.info index 95c8f6a4..407ebd68 100644 --- a/3.0/modules/ecard/module.info +++ b/3.0/modules/ecard/module.info @@ -1,4 +1,4 @@ name = "E-Card" description = "Send a photo as a postcard" -version = 4 +version = 5 diff --git a/3.1/modules/ecard/helpers/ecard_installer.php b/3.1/modules/ecard/helpers/ecard_installer.php index d9887094..a9a6934f 100644 --- a/3.1/modules/ecard/helpers/ecard_installer.php +++ b/3.1/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", 4); + module::set_version("ecard", 5); } } diff --git a/3.1/modules/ecard/module.info b/3.1/modules/ecard/module.info index 95c8f6a4..407ebd68 100644 --- a/3.1/modules/ecard/module.info +++ b/3.1/modules/ecard/module.info @@ -1,4 +1,4 @@ name = "E-Card" description = "Send a photo as a postcard" -version = 4 +version = 5 From 2c9a045ad909462194514bda21bd4bc194b88a58 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 8 Jan 2011 18:37:52 -0800 Subject: [PATCH 221/300] Move the HTMLPurifier code from lib to vendor to be compliant with our packaging structure --- 3.0/modules/purifier/helpers/purifier.php | 2 +- .../HTMLPurifier/HTMLPurifier.auto.php | 0 .../HTMLPurifier/HTMLPurifier.autoload.php | 0 .../HTMLPurifier/HTMLPurifier.func.php | 0 .../HTMLPurifier/HTMLPurifier.includes.php | 0 .../HTMLPurifier/HTMLPurifier.kses.php | 0 .../HTMLPurifier/HTMLPurifier.path.php | 0 .../{lib => vendor}/HTMLPurifier/HTMLPurifier.php | 0 .../HTMLPurifier/HTMLPurifier.safe-includes.php | 0 .../HTMLPurifier/HTMLPurifier/AttrCollections.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/CSS.php | 0 .../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 0 .../HTMLPurifier/AttrDef/CSS/Background.php | 0 .../HTMLPurifier/AttrDef/CSS/BackgroundPosition.php | 0 .../HTMLPurifier/AttrDef/CSS/Border.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php | 0 .../HTMLPurifier/AttrDef/CSS/Composite.php | 0 .../AttrDef/CSS/DenyElementDecorator.php | 0 .../HTMLPurifier/AttrDef/CSS/Filter.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php | 0 .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 0 .../HTMLPurifier/AttrDef/CSS/ImportantDecorator.php | 0 .../HTMLPurifier/AttrDef/CSS/Length.php | 0 .../HTMLPurifier/AttrDef/CSS/ListStyle.php | 0 .../HTMLPurifier/AttrDef/CSS/Multiple.php | 0 .../HTMLPurifier/AttrDef/CSS/Number.php | 0 .../HTMLPurifier/AttrDef/CSS/Percentage.php | 0 .../HTMLPurifier/AttrDef/CSS/TextDecoration.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/Enum.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php | 0 .../HTMLPurifier/AttrDef/HTML/Class.php | 0 .../HTMLPurifier/AttrDef/HTML/Color.php | 0 .../HTMLPurifier/AttrDef/HTML/FrameTarget.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php | 0 .../HTMLPurifier/AttrDef/HTML/Length.php | 0 .../HTMLPurifier/AttrDef/HTML/LinkTypes.php | 0 .../HTMLPurifier/AttrDef/HTML/MultiLength.php | 0 .../HTMLPurifier/AttrDef/HTML/Nmtokens.php | 0 .../HTMLPurifier/AttrDef/HTML/Pixels.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/Integer.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/Lang.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/Switch.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/Text.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/URI.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php | 0 .../HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php | 0 .../HTMLPurifier/HTMLPurifier/AttrTransform.php | 0 .../HTMLPurifier/AttrTransform/Background.php | 0 .../HTMLPurifier/AttrTransform/BdoDir.php | 0 .../HTMLPurifier/AttrTransform/BgColor.php | 0 .../HTMLPurifier/AttrTransform/BoolToCSS.php | 0 .../HTMLPurifier/AttrTransform/Border.php | 0 .../HTMLPurifier/AttrTransform/EnumToCSS.php | 0 .../HTMLPurifier/AttrTransform/ImgRequired.php | 0 .../HTMLPurifier/AttrTransform/ImgSpace.php | 0 .../HTMLPurifier/AttrTransform/Input.php | 0 .../HTMLPurifier/AttrTransform/Lang.php | 0 .../HTMLPurifier/AttrTransform/Length.php | 0 .../HTMLPurifier/AttrTransform/Name.php | 0 .../HTMLPurifier/AttrTransform/NameSync.php | 0 .../HTMLPurifier/AttrTransform/SafeEmbed.php | 0 .../HTMLPurifier/AttrTransform/SafeObject.php | 0 .../HTMLPurifier/AttrTransform/SafeParam.php | 0 .../HTMLPurifier/AttrTransform/ScriptRequired.php | 0 .../HTMLPurifier/AttrTransform/Textarea.php | 0 .../HTMLPurifier/HTMLPurifier/AttrTypes.php | 0 .../HTMLPurifier/HTMLPurifier/AttrValidator.php | 0 .../HTMLPurifier/HTMLPurifier/Bootstrap.php | 0 .../HTMLPurifier/HTMLPurifier/CSSDefinition.php | 0 .../HTMLPurifier/HTMLPurifier/ChildDef.php | 0 .../HTMLPurifier/ChildDef/Chameleon.php | 0 .../HTMLPurifier/HTMLPurifier/ChildDef/Custom.php | 0 .../HTMLPurifier/HTMLPurifier/ChildDef/Empty.php | 0 .../HTMLPurifier/HTMLPurifier/ChildDef/Optional.php | 0 .../HTMLPurifier/HTMLPurifier/ChildDef/Required.php | 0 .../HTMLPurifier/ChildDef/StrictBlockquote.php | 0 .../HTMLPurifier/HTMLPurifier/ChildDef/Table.php | 0 .../HTMLPurifier/HTMLPurifier/Config.php | 0 .../HTMLPurifier/HTMLPurifier/ConfigSchema.php | 0 .../ConfigSchema/Builder/ConfigSchema.php | 0 .../HTMLPurifier/ConfigSchema/Builder/Xml.php | 0 .../HTMLPurifier/ConfigSchema/Exception.php | 0 .../HTMLPurifier/ConfigSchema/Interchange.php | 0 .../ConfigSchema/Interchange/Directive.php | 0 .../HTMLPurifier/ConfigSchema/Interchange/Id.php | 0 .../ConfigSchema/InterchangeBuilder.php | 0 .../HTMLPurifier/ConfigSchema/Validator.php | 0 .../HTMLPurifier/ConfigSchema/ValidatorAtom.php | 0 .../HTMLPurifier/ConfigSchema/schema.ser | Bin .../ConfigSchema/schema/Attr.AllowedClasses.txt | 0 .../schema/Attr.AllowedFrameTargets.txt | 0 .../ConfigSchema/schema/Attr.AllowedRel.txt | 0 .../ConfigSchema/schema/Attr.AllowedRev.txt | 0 .../ConfigSchema/schema/Attr.ClassUseCDATA.txt | 0 .../ConfigSchema/schema/Attr.DefaultImageAlt.txt | 0 .../schema/Attr.DefaultInvalidImage.txt | 0 .../schema/Attr.DefaultInvalidImageAlt.txt | 0 .../ConfigSchema/schema/Attr.DefaultTextDir.txt | 0 .../ConfigSchema/schema/Attr.EnableID.txt | 0 .../ConfigSchema/schema/Attr.ForbiddenClasses.txt | 0 .../ConfigSchema/schema/Attr.IDBlacklist.txt | 0 .../ConfigSchema/schema/Attr.IDBlacklistRegexp.txt | 0 .../ConfigSchema/schema/Attr.IDPrefix.txt | 0 .../ConfigSchema/schema/Attr.IDPrefixLocal.txt | 0 .../schema/AutoFormat.AutoParagraph.txt | 0 .../ConfigSchema/schema/AutoFormat.Custom.txt | 0 .../schema/AutoFormat.DisplayLinkURI.txt | 0 .../ConfigSchema/schema/AutoFormat.Linkify.txt | 0 .../schema/AutoFormat.PurifierLinkify.DocURL.txt | 0 .../schema/AutoFormat.PurifierLinkify.txt | 0 ...AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt | 0 .../schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt | 0 .../ConfigSchema/schema/AutoFormat.RemoveEmpty.txt | 0 .../ConfigSchema/schema/CSS.AllowImportant.txt | 0 .../ConfigSchema/schema/CSS.AllowTricky.txt | 0 .../ConfigSchema/schema/CSS.AllowedProperties.txt | 0 .../ConfigSchema/schema/CSS.DefinitionRev.txt | 0 .../ConfigSchema/schema/CSS.MaxImgLength.txt | 0 .../ConfigSchema/schema/CSS.Proprietary.txt | 0 .../ConfigSchema/schema/Cache.DefinitionImpl.txt | 0 .../ConfigSchema/schema/Cache.SerializerPath.txt | 0 .../ConfigSchema/schema/Core.AggressivelyFixLt.txt | 0 .../ConfigSchema/schema/Core.CollectErrors.txt | 0 .../ConfigSchema/schema/Core.ColorKeywords.txt | 0 .../schema/Core.ConvertDocumentToFragment.txt | 0 .../schema/Core.DirectLexLineNumberSyncInterval.txt | 0 .../ConfigSchema/schema/Core.Encoding.txt | 0 .../schema/Core.EscapeInvalidChildren.txt | 0 .../ConfigSchema/schema/Core.EscapeInvalidTags.txt | 0 .../schema/Core.EscapeNonASCIICharacters.txt | 0 .../ConfigSchema/schema/Core.HiddenElements.txt | 0 .../ConfigSchema/schema/Core.Language.txt | 0 .../ConfigSchema/schema/Core.LexerImpl.txt | 0 .../schema/Core.MaintainLineNumbers.txt | 0 .../ConfigSchema/schema/Core.RemoveInvalidImg.txt | 0 .../schema/Core.RemoveScriptContents.txt | 0 .../ConfigSchema/schema/Filter.Custom.txt | 0 .../schema/Filter.ExtractStyleBlocks.Escaping.txt | 0 .../schema/Filter.ExtractStyleBlocks.Scope.txt | 0 .../schema/Filter.ExtractStyleBlocks.TidyImpl.txt | 0 .../schema/Filter.ExtractStyleBlocks.txt | 0 .../ConfigSchema/schema/Filter.YouTube.txt | 0 .../ConfigSchema/schema/HTML.Allowed.txt | 0 .../ConfigSchema/schema/HTML.AllowedAttributes.txt | 0 .../ConfigSchema/schema/HTML.AllowedElements.txt | 0 .../ConfigSchema/schema/HTML.AllowedModules.txt | 0 .../ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt | 0 .../ConfigSchema/schema/HTML.BlockWrapper.txt | 0 .../ConfigSchema/schema/HTML.CoreModules.txt | 0 .../ConfigSchema/schema/HTML.CustomDoctype.txt | 0 .../ConfigSchema/schema/HTML.DefinitionID.txt | 0 .../ConfigSchema/schema/HTML.DefinitionRev.txt | 0 .../ConfigSchema/schema/HTML.Doctype.txt | 0 .../schema/HTML.ForbiddenAttributes.txt | 0 .../ConfigSchema/schema/HTML.ForbiddenElements.txt | 0 .../ConfigSchema/schema/HTML.MaxImgLength.txt | 0 .../ConfigSchema/schema/HTML.Parent.txt | 0 .../ConfigSchema/schema/HTML.Proprietary.txt | 0 .../ConfigSchema/schema/HTML.SafeEmbed.txt | 0 .../ConfigSchema/schema/HTML.SafeObject.txt | 0 .../ConfigSchema/schema/HTML.Strict.txt | 0 .../ConfigSchema/schema/HTML.TidyAdd.txt | 0 .../ConfigSchema/schema/HTML.TidyLevel.txt | 0 .../ConfigSchema/schema/HTML.TidyRemove.txt | 0 .../ConfigSchema/schema/HTML.Trusted.txt | 0 .../HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt | 0 .../schema/Output.CommentScriptContents.txt | 0 .../ConfigSchema/schema/Output.Newline.txt | 0 .../ConfigSchema/schema/Output.SortAttr.txt | 0 .../ConfigSchema/schema/Output.TidyFormat.txt | 0 .../ConfigSchema/schema/Test.ForceNoIconv.txt | 0 .../ConfigSchema/schema/URI.AllowedSchemes.txt | 0 .../HTMLPurifier/ConfigSchema/schema/URI.Base.txt | 0 .../ConfigSchema/schema/URI.DefaultScheme.txt | 0 .../ConfigSchema/schema/URI.DefinitionID.txt | 0 .../ConfigSchema/schema/URI.DefinitionRev.txt | 0 .../ConfigSchema/schema/URI.Disable.txt | 0 .../ConfigSchema/schema/URI.DisableExternal.txt | 0 .../schema/URI.DisableExternalResources.txt | 0 .../ConfigSchema/schema/URI.DisableResources.txt | 0 .../HTMLPurifier/ConfigSchema/schema/URI.Host.txt | 0 .../ConfigSchema/schema/URI.HostBlacklist.txt | 0 .../ConfigSchema/schema/URI.MakeAbsolute.txt | 0 .../HTMLPurifier/ConfigSchema/schema/URI.Munge.txt | 0 .../ConfigSchema/schema/URI.MungeResources.txt | 0 .../ConfigSchema/schema/URI.MungeSecretKey.txt | 0 .../schema/URI.OverrideAllowedSchemes.txt | 0 .../HTMLPurifier/ConfigSchema/schema/info.ini | 0 .../HTMLPurifier/HTMLPurifier/ContentSets.php | 0 .../HTMLPurifier/HTMLPurifier/Context.php | 0 .../HTMLPurifier/HTMLPurifier/Definition.php | 0 .../HTMLPurifier/HTMLPurifier/DefinitionCache.php | 0 .../HTMLPurifier/DefinitionCache/Decorator.php | 0 .../DefinitionCache/Decorator/Cleanup.php | 0 .../DefinitionCache/Decorator/Memory.php | 0 .../DefinitionCache/Decorator/Template.php.in | 0 .../HTMLPurifier/DefinitionCache/Null.php | 0 .../HTMLPurifier/DefinitionCache/Serializer.php | 0 .../HTMLPurifier/DefinitionCache/Serializer/README | 0 .../HTMLPurifier/DefinitionCacheFactory.php | 0 .../HTMLPurifier/HTMLPurifier/Doctype.php | 0 .../HTMLPurifier/HTMLPurifier/DoctypeRegistry.php | 0 .../HTMLPurifier/HTMLPurifier/ElementDef.php | 0 .../HTMLPurifier/HTMLPurifier/Encoder.php | 0 .../HTMLPurifier/HTMLPurifier/EntityLookup.php | 0 .../HTMLPurifier/EntityLookup/entities.ser | 0 .../HTMLPurifier/HTMLPurifier/EntityParser.php | 0 .../HTMLPurifier/HTMLPurifier/ErrorCollector.php | 0 .../HTMLPurifier/HTMLPurifier/ErrorStruct.php | 0 .../HTMLPurifier/HTMLPurifier/Exception.php | 0 .../HTMLPurifier/HTMLPurifier/Filter.php | 0 .../HTMLPurifier/Filter/ExtractStyleBlocks.php | 0 .../HTMLPurifier/HTMLPurifier/Filter/YouTube.php | 0 .../HTMLPurifier/HTMLPurifier/Generator.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLDefinition.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php | 0 .../HTMLPurifier/HTMLModule/CommonAttributes.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php | 0 .../HTMLPurifier/HTMLModule/Hypertext.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Image.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/List.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Name.php | 0 .../HTMLModule/NonXMLCommonAttributes.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Object.php | 0 .../HTMLPurifier/HTMLModule/Presentation.php | 0 .../HTMLPurifier/HTMLModule/Proprietary.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php | 0 .../HTMLPurifier/HTMLModule/SafeEmbed.php | 0 .../HTMLPurifier/HTMLModule/SafeObject.php | 0 .../HTMLPurifier/HTMLModule/Scripting.php | 0 .../HTMLPurifier/HTMLModule/StyleAttribute.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Target.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Text.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php | 0 .../HTMLPurifier/HTMLModule/Tidy/Name.php | 0 .../HTMLPurifier/HTMLModule/Tidy/Proprietary.php | 0 .../HTMLPurifier/HTMLModule/Tidy/Strict.php | 0 .../HTMLPurifier/HTMLModule/Tidy/Transitional.php | 0 .../HTMLPurifier/HTMLModule/Tidy/XHTML.php | 0 .../HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php | 0 .../HTMLPurifier/HTMLModule/XMLCommonAttributes.php | 0 .../HTMLPurifier/HTMLPurifier/HTMLModuleManager.php | 0 .../HTMLPurifier/HTMLPurifier/IDAccumulator.php | 0 .../HTMLPurifier/HTMLPurifier/Injector.php | 0 .../HTMLPurifier/Injector/AutoParagraph.php | 0 .../HTMLPurifier/Injector/DisplayLinkURI.php | 0 .../HTMLPurifier/HTMLPurifier/Injector/Linkify.php | 0 .../HTMLPurifier/Injector/PurifierLinkify.php | 0 .../HTMLPurifier/Injector/RemoveEmpty.php | 0 .../HTMLPurifier/Injector/SafeObject.php | 0 .../HTMLPurifier/HTMLPurifier/Language.php | 0 .../HTMLPurifier/Language/classes/en-x-test.php | 0 .../HTMLPurifier/Language/messages/en-x-test.php | 0 .../Language/messages/en-x-testmini.php | 0 .../HTMLPurifier/Language/messages/en.php | 0 .../HTMLPurifier/HTMLPurifier/LanguageFactory.php | 0 .../HTMLPurifier/HTMLPurifier/Length.php | 0 .../HTMLPurifier/HTMLPurifier/Lexer.php | 0 .../HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php | 0 .../HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php | 0 .../HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php | 0 .../HTMLPurifier/HTMLPurifier/Lexer/PH5P.php | 0 .../HTMLPurifier/HTMLPurifier/PercentEncoder.php | 0 .../HTMLPurifier/HTMLPurifier/Printer.php | 0 .../HTMLPurifier/Printer/CSSDefinition.php | 0 .../HTMLPurifier/Printer/ConfigForm.css | 0 .../HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js | 0 .../HTMLPurifier/Printer/ConfigForm.php | 0 .../HTMLPurifier/Printer/HTMLDefinition.php | 0 .../HTMLPurifier/HTMLPurifier/PropertyList.php | 0 .../HTMLPurifier/PropertyListIterator.php | 0 .../HTMLPurifier/HTMLPurifier/Strategy.php | 0 .../HTMLPurifier/Strategy/Composite.php | 0 .../HTMLPurifier/HTMLPurifier/Strategy/Core.php | 0 .../HTMLPurifier/Strategy/FixNesting.php | 0 .../HTMLPurifier/Strategy/MakeWellFormed.php | 0 .../HTMLPurifier/Strategy/RemoveForeignElements.php | 0 .../HTMLPurifier/Strategy/ValidateAttributes.php | 0 .../HTMLPurifier/HTMLPurifier/StringHash.php | 0 .../HTMLPurifier/HTMLPurifier/StringHashParser.php | 0 .../HTMLPurifier/HTMLPurifier/TagTransform.php | 0 .../HTMLPurifier/HTMLPurifier/TagTransform/Font.php | 0 .../HTMLPurifier/TagTransform/Simple.php | 0 .../HTMLPurifier/HTMLPurifier/Token.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Comment.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Empty.php | 0 .../HTMLPurifier/HTMLPurifier/Token/End.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Start.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Tag.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Text.php | 0 .../HTMLPurifier/HTMLPurifier/TokenFactory.php | 0 .../HTMLPurifier/HTMLPurifier/URI.php | 0 .../HTMLPurifier/HTMLPurifier/URIDefinition.php | 0 .../HTMLPurifier/HTMLPurifier/URIFilter.php | 0 .../HTMLPurifier/URIFilter/DisableExternal.php | 0 .../URIFilter/DisableExternalResources.php | 0 .../HTMLPurifier/URIFilter/HostBlacklist.php | 0 .../HTMLPurifier/URIFilter/MakeAbsolute.php | 0 .../HTMLPurifier/HTMLPurifier/URIFilter/Munge.php | 0 .../HTMLPurifier/HTMLPurifier/URIParser.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme/ftp.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme/http.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme/https.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme/mailto.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme/news.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme/nntp.php | 0 .../HTMLPurifier/HTMLPurifier/URISchemeRegistry.php | 0 .../HTMLPurifier/HTMLPurifier/UnitConverter.php | 0 .../HTMLPurifier/HTMLPurifier/VarParser.php | 0 .../HTMLPurifier/VarParser/Flexible.php | 0 .../HTMLPurifier/HTMLPurifier/VarParser/Native.php | 0 .../HTMLPurifier/VarParserException.php | 0 .../HTMLPurifier/HTMLPurifierLicense | 0 323 files changed, 1 insertion(+), 1 deletion(-) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.auto.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.autoload.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.func.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.includes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.kses.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.path.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.safe-includes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrCollections.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Text.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTypes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrValidator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Bootstrap.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/CSSDefinition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Required.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Table.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Config.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ContentSets.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Context.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Definition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Doctype.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ElementDef.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Encoder.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/EntityLookup.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/EntityParser.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ErrorCollector.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ErrorStruct.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Exception.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Filter.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Filter/YouTube.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Generator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLDefinition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/List.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/IDAccumulator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/Linkify.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/messages/en.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/LanguageFactory.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Length.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/PercentEncoder.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/PropertyList.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/PropertyListIterator.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/Composite.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/Core.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/StringHash.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/StringHashParser.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TagTransform.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TagTransform/Font.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Comment.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Empty.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/End.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Start.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Tag.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Text.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TokenFactory.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URI.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIDefinition.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIParser.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/http.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/https.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/news.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/UnitConverter.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParser.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParser/Native.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParserException.php (100%) rename 3.0/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifierLicense (100%) diff --git a/3.0/modules/purifier/helpers/purifier.php b/3.0/modules/purifier/helpers/purifier.php index 97742b50..2af53180 100644 --- a/3.0/modules/purifier/helpers/purifier.php +++ b/3.0/modules/purifier/helpers/purifier.php @@ -21,7 +21,7 @@ class purifier { static function purify($dirty_html) { if (!isset(self::$_purifier)) { - require_once(MODPATH . "purifier/lib/HTMLPurifier/HTMLPurifier.auto.php"); + require_once(MODPATH . "purifier/vendor/HTMLPurifier/HTMLPurifier.auto.php"); $config = HTMLPurifier_Config::createDefault(); foreach (Kohana::config("purifier") as $category => $key_value) { foreach ($key_value as $key => $value) { diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.auto.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.auto.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.auto.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.auto.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.autoload.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.autoload.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.autoload.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.autoload.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.func.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.func.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.func.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.func.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.includes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.includes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.kses.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.kses.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.kses.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.kses.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.path.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.path.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.path.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.path.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.safe-includes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier.safe-includes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrCollections.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrCollections.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrCollections.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrCollections.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Text.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Text.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTypes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTypes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTypes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTypes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrValidator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrValidator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrValidator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrValidator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Bootstrap.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Bootstrap.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Bootstrap.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Bootstrap.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/CSSDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/CSSDefinition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Required.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Required.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Table.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Table.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Config.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Config.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ContentSets.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ContentSets.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ContentSets.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ContentSets.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Context.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Context.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Context.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Context.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Definition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Definition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Definition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Definition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Doctype.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Doctype.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Doctype.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Doctype.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ElementDef.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ElementDef.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Encoder.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Encoder.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Encoder.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Encoder.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityParser.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityParser.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityParser.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityParser.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorCollector.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorCollector.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorCollector.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorCollector.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorStruct.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorStruct.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorStruct.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorStruct.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Exception.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Exception.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Exception.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Exception.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Generator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Generator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/IDAccumulator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/IDAccumulator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/IDAccumulator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/IDAccumulator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/Linkify.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/Linkify.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/LanguageFactory.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/LanguageFactory.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/LanguageFactory.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/LanguageFactory.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Length.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Length.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Length.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Length.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PercentEncoder.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PercentEncoder.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PercentEncoder.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PercentEncoder.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyList.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyList.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyList.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyList.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyListIterator.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyListIterator.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Composite.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Composite.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Composite.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Composite.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Core.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Core.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Core.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Core.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/StringHash.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/StringHash.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/StringHash.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/StringHash.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/StringHashParser.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/StringHashParser.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/StringHashParser.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/StringHashParser.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TagTransform.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TagTransform.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TagTransform.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TagTransform.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TagTransform/Font.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TagTransform/Font.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TagTransform/Font.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TagTransform/Font.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Comment.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Comment.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Comment.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Comment.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Empty.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Empty.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Empty.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Empty.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/End.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/End.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/End.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/End.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Start.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Start.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Start.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Start.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Tag.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Tag.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Tag.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Tag.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Text.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Text.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Token/Text.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Token/Text.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TokenFactory.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TokenFactory.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/TokenFactory.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/TokenFactory.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URI.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URI.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URI.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URI.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIDefinition.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIDefinition.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIDefinition.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIParser.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIParser.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIParser.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIParser.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/http.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/http.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/http.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/http.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/https.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/https.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/https.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/https.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/news.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/news.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/news.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/news.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/UnitConverter.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/UnitConverter.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/UnitConverter.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/UnitConverter.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParser.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParser.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParser/Native.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Native.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParser/Native.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Native.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParserException.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParserException.php similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifier/VarParserException.php rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParserException.php diff --git a/3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifierLicense b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifierLicense similarity index 100% rename from 3.0/modules/purifier/lib/HTMLPurifier/HTMLPurifierLicense rename to 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifierLicense From 0005833e8e6f09170b19a9a3cc33b56a6a271275 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 8 Jan 2011 18:39:08 -0800 Subject: [PATCH 222/300] Update HTMLPurifier to 4.2.0 --- .../HTMLPurifier/HTMLPurifier.includes.php | 6 +- .../vendor/HTMLPurifier/HTMLPurifier.php | 6 +- .../HTMLPurifier.safe-includes.php | 4 + .../HTMLPurifier/HTMLPurifier/AttrDef.php | 36 +++++++ .../AttrDef/CSS/BackgroundPosition.php | 21 ++-- .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 52 ++++------ .../HTMLPurifier/AttrDef/CSS/URI.php | 12 +-- .../AttrTransform/ImgRequired.php | 3 +- .../HTMLPurifier/AttrTransform/SafeParam.php | 13 +++ .../HTMLPurifier/CSSDefinition.php | 19 +++- .../HTMLPurifier/HTMLPurifier/Config.php | 2 +- .../HTMLPurifier/ConfigSchema/schema.ser | Bin 12999 -> 13701 bytes ...utoFormat.RemoveSpansWithoutAttributes.txt | 11 +++ .../schema/CSS.ForbiddenProperties.txt | 13 +++ .../schema/Core.NormalizeNewlines.txt | 11 +++ .../Core.RemoveProcessingInstructions.txt | 11 +++ .../ConfigSchema/schema/Filter.YouTube.txt | 5 + .../ConfigSchema/schema/HTML.Allowed.txt | 11 ++- .../schema/HTML.AllowedElements.txt | 17 ++-- .../schema/HTML.FlashAllowFullScreen.txt | 11 +++ .../ConfigSchema/schema/HTML.SafeEmbed.txt | 7 +- .../ConfigSchema/schema/HTML.SafeObject.txt | 7 +- .../schema/Output.FlashCompat.txt | 11 +++ .../schema/URI.AllowedSchemes.txt | 2 + .../schema/URI.DisableResources.txt | 7 +- .../HTMLPurifier/HTMLPurifier/ElementDef.php | 7 ++ .../HTMLPurifier/Filter/YouTube.php | 10 +- .../HTMLPurifier/HTMLPurifier/Generator.php | 56 ++++++++++- .../HTMLPurifier/HTMLDefinition.php | 7 +- .../HTMLPurifier/HTMLModule/List.php | 6 +- .../HTMLPurifier/HTMLModule/SafeEmbed.php | 1 + .../HTMLPurifier/HTMLModule/SafeObject.php | 5 +- .../HTMLModule/Tidy/Proprietary.php | 1 + .../HTMLPurifier/Injector/AutoParagraph.php | 21 ++-- .../Injector/RemoveSpansWithoutAttributes.php | 60 +++++++++++ .../HTMLPurifier/Injector/SafeObject.php | 6 +- .../HTMLPurifier/Language/messages/en.php | 1 + .../HTMLPurifier/HTMLPurifier/Lexer.php | 34 ++++++- .../HTMLPurifier/Lexer/DirectLex.php | 2 +- .../HTMLPurifier/Lexer/PEARSax3.php | 37 ++++++- .../HTMLPurifier/HTMLPurifier/Lexer/PH5P.php | 4 +- .../HTMLPurifier/Strategy/MakeWellFormed.php | 18 ++++ .../URIFilter/DisableResources.php | 11 +++ .../HTMLPurifier/URIScheme/data.php | 93 ++++++++++++++++++ .../HTMLPurifier/URIScheme/file.php | 26 +++++ .../HTMLPurifier/VarParser/Flexible.php | 9 +- 46 files changed, 599 insertions(+), 114 deletions(-) create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableResources.php create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/data.php create mode 100644 3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/file.php diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php index e57f2ab3..08737c20 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php @@ -7,7 +7,7 @@ * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * FILE, changes will be overwritten the next time the script is run. * - * @version 4.0.0 + * @version 4.2.0 * * @warning * You must *not* include any other HTML Purifier files before this file, @@ -176,6 +176,7 @@ require 'HTMLPurifier/Injector/DisplayLinkURI.php'; require 'HTMLPurifier/Injector/Linkify.php'; require 'HTMLPurifier/Injector/PurifierLinkify.php'; require 'HTMLPurifier/Injector/RemoveEmpty.php'; +require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; require 'HTMLPurifier/Injector/SafeObject.php'; require 'HTMLPurifier/Lexer/DOMLex.php'; require 'HTMLPurifier/Lexer/DirectLex.php'; @@ -195,9 +196,12 @@ require 'HTMLPurifier/Token/Start.php'; require 'HTMLPurifier/Token/Text.php'; require 'HTMLPurifier/URIFilter/DisableExternal.php'; require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; +require 'HTMLPurifier/URIFilter/DisableResources.php'; require 'HTMLPurifier/URIFilter/HostBlacklist.php'; require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; require 'HTMLPurifier/URIFilter/Munge.php'; +require 'HTMLPurifier/URIScheme/data.php'; +require 'HTMLPurifier/URIScheme/file.php'; require 'HTMLPurifier/URIScheme/ftp.php'; require 'HTMLPurifier/URIScheme/http.php'; require 'HTMLPurifier/URIScheme/https.php'; diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php index 71e90632..0430ad39 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php @@ -19,7 +19,7 @@ */ /* - HTML Purifier 4.0.0 - Standards Compliant HTML Filtering + HTML Purifier 4.2.0 - Standards Compliant HTML Filtering Copyright (C) 2006-2008 Edward Z. Yang This library is free software; you can redistribute it and/or @@ -55,10 +55,10 @@ class HTMLPurifier { /** Version of HTML Purifier */ - public $version = '4.0.0'; + public $version = '4.2.0'; /** Constant with version of HTML Purifier */ - const VERSION = '4.0.0'; + const VERSION = '4.2.0'; /** Global configuration object */ public $config; diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php index 5f0e1d8f..899a1f2e 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php @@ -170,6 +170,7 @@ require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php'; require_once $__dir . '/HTMLPurifier/Injector/Linkify.php'; require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php'; require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php'; +require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php'; require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php'; require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php'; @@ -189,9 +190,12 @@ require_once $__dir . '/HTMLPurifier/Token/Start.php'; require_once $__dir . '/HTMLPurifier/Token/Text.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php'; require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/data.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/file.php'; require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php index 7fac54e8..6f82201e 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php @@ -82,6 +82,42 @@ abstract class HTMLPurifier_AttrDef return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); } + /** + * Parses a possibly escaped CSS string and returns the "pure" + * version of it. + */ + protected function expandCSSEscape($string) { + // flexibly parse it + $ret = ''; + for ($i = 0, $c = strlen($string); $i < $c; $i++) { + if ($string[$i] === '\\') { + $i++; + if ($i >= $c) { + $ret .= '\\'; + break; + } + if (ctype_xdigit($string[$i])) { + $code = $string[$i]; + for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { + if (!ctype_xdigit($string[$i])) break; + $code .= $string[$i]; + } + // We have to be extremely careful when adding + // new characters, to make sure we're not breaking + // the encoding. + $char = HTMLPurifier_Encoder::unichr(hexdec($code)); + if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; + $ret .= $char; + if ($i < $c && trim($string[$i]) !== '') $i--; + continue; + } + if ($string[$i] === "\n") continue; + } + $ret .= $string[$i]; + } + return $ret; + } + } // vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php index e067a754..665321e3 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php @@ -59,7 +59,8 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef $keywords = array(); $keywords['h'] = false; // left, right $keywords['v'] = false; // top, bottom - $keywords['c'] = false; // center + $keywords['ch'] = false; // center (first word) + $keywords['cv'] = false; // center (second word) $measures = array(); $i = 0; @@ -79,6 +80,13 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef $lbit = ctype_lower($bit) ? $bit : strtolower($bit); if (isset($lookup[$lbit])) { $status = $lookup[$lbit]; + if ($status == 'c') { + if ($i == 0) { + $status = 'ch'; + } else { + $status = 'cv'; + } + } $keywords[$status] = $lbit; $i++; } @@ -101,20 +109,19 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef if (!$i) return false; // no valid values were caught - $ret = array(); // first keyword if ($keywords['h']) $ret[] = $keywords['h']; - elseif (count($measures)) $ret[] = array_shift($measures); - elseif ($keywords['c']) { - $ret[] = $keywords['c']; - $keywords['c'] = false; // prevent re-use: center = center center + elseif ($keywords['ch']) { + $ret[] = $keywords['ch']; + $keywords['cv'] = false; // prevent re-use: center = center center } + elseif (count($measures)) $ret[] = array_shift($measures); if ($keywords['v']) $ret[] = $keywords['v']; + elseif ($keywords['cv']) $ret[] = $keywords['cv']; elseif (count($measures)) $ret[] = array_shift($measures); - elseif ($keywords['c']) $ret[] = $keywords['c']; if (empty($ret)) return false; return implode(' ', $ret); diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php index 33435c76..f1ceec4a 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php @@ -34,37 +34,10 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef $quote = $font[0]; if ($font[$length - 1] !== $quote) continue; $font = substr($font, 1, $length - 2); - - $new_font = ''; - for ($i = 0, $c = strlen($font); $i < $c; $i++) { - if ($font[$i] === '\\') { - $i++; - if ($i >= $c) { - $new_font .= '\\'; - break; - } - if (ctype_xdigit($font[$i])) { - $code = $font[$i]; - for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { - if (!ctype_xdigit($font[$i])) break; - $code .= $font[$i]; - } - // We have to be extremely careful when adding - // new characters, to make sure we're not breaking - // the encoding. - $char = HTMLPurifier_Encoder::unichr(hexdec($code)); - if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; - $new_font .= $char; - if ($i < $c && trim($font[$i]) !== '') $i--; - continue; - } - if ($font[$i] === "\n") continue; - } - $new_font .= $font[$i]; - } - - $font = $new_font; } + + $font = $this->expandCSSEscape($font); + // $font is a pure representation of the font name if (ctype_alnum($font) && $font !== '') { @@ -73,12 +46,21 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef continue; } - // complicated font, requires quoting + // bugger out on whitespace. form feed (0C) really + // shouldn't show up regardless + $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); - // armor single quotes and new lines - $font = str_replace("\\", "\\\\", $font); - $font = str_replace("'", "\\'", $font); - $final .= "'$font', "; + // These ugly transforms don't pose a security + // risk (as \\ and \" might). We could try to be clever and + // use single-quote wrapping when there is a double quote + // present, but I have choosen not to implement that. + // (warning: this code relies on the selection of quotation + // mark below) + $font = str_replace('\\', '\\5C ', $font); + $font = str_replace('"', '\\22 ', $font); + + // complicated font, requires quoting + $final .= "\"$font\", "; // note that this will later get turned into " } $final = rtrim($final, ', '); if ($final === '') return false; diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php index d09c87bc..98df033d 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php @@ -34,20 +34,16 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI $uri = substr($uri, 1, $new_length - 1); } - $keys = array( '(', ')', ',', ' ', '"', "'"); - $values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'"); - $uri = str_replace($values, $keys, $uri); + $uri = $this->expandCSSEscape($uri); $result = parent::validate($uri, $config, $context); if ($result === false) return false; - // escape necessary characters according to CSS spec - // except for the comma, none of these should appear in the - // URI at all - $result = str_replace($keys, $values, $result); + // extra sanity check; should have been done by URI + $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result); - return "url($result)"; + return "url(\"$result\")"; } diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php index a1e5a83a..3d09eca3 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php @@ -24,7 +24,8 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform if ($src) { $alt = $config->get('Attr.DefaultImageAlt'); if ($alt === null) { - $attr['alt'] = basename($attr['src']); + // truncate if the alt is too long + $attr['alt'] = substr(basename($attr['src']),0,40); } else { $attr['alt'] = $alt; } diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php index e677feae..d14390bc 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php @@ -33,12 +33,25 @@ class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform case 'allowNetworking': $attr['value'] = 'internal'; break; + case 'allowFullScreen': + if ($config->get('HTML.FlashAllowFullScreen')) { + $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false'; + } else { + $attr['value'] = 'false'; + } + break; case 'wmode': $attr['value'] = 'window'; break; case 'movie': + case 'src': + $attr['name'] = "movie"; $attr['value'] = $this->uri->validate($attr['value'], $config, $context); break; + case 'flashvars': + // we're going to allow arbitrary inputs to the SWF, on + // the reasoning that it could only hack the SWF, not us. + break; // add other cases to support other param name/value pairs default: $attr['name'] = $attr['value'] = null; diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php index 17bf9931..09afc1f1 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php @@ -272,20 +272,29 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition // setup allowed elements $support = "(for information on implementing this, see the ". "support forums) "; - $allowed_attributes = $config->get('CSS.AllowedProperties'); - if ($allowed_attributes !== null) { + $allowed_properties = $config->get('CSS.AllowedProperties'); + if ($allowed_properties !== null) { foreach ($this->info as $name => $d) { - if(!isset($allowed_attributes[$name])) unset($this->info[$name]); - unset($allowed_attributes[$name]); + if(!isset($allowed_properties[$name])) unset($this->info[$name]); + unset($allowed_properties[$name]); } // emit errors - foreach ($allowed_attributes as $name => $d) { + foreach ($allowed_properties as $name => $d) { // :TODO: Is this htmlspecialchars() call really necessary? $name = htmlspecialchars($name); trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); } } + $forbidden_properties = $config->get('CSS.ForbiddenProperties'); + if ($forbidden_properties !== null) { + foreach ($this->info as $name => $d) { + if (isset($forbidden_properties[$name])) { + unset($this->info[$name]); + } + } + } + } } diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php index 28529e7f..ada1b701 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php @@ -20,7 +20,7 @@ class HTMLPurifier_Config /** * HTML Purifier's version */ - public $version = '4.0.0'; + public $version = '4.2.0'; /** * Bool indicator whether or not to automatically finalize diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser index bbf12f9c3e7392aa8143727d2485f6f9ad1f97e1..978089c6291e7d828f6233ac632cedd8a98fdfd7 100644 GIT binary patch delta 810 zcmX?}+L}GVl-bb0Wb$Aui0oCOfrRw>C49>}{O7%-E&&kXKDNC|4ocvH+Mavju7Dx@mR-j4Asl~;adFh^c z#U(|h$t9Wjd0^!RoAWrHGAWr_DS3qW`slgkBo=2l=H%p;yOrkT1Sc1zrsjc6HL#v6 zsH?EKfV+uN+QLf7zqF*Fv;?BsIX|}`u>`8_~#?WZy$&Z;!CkLyEZJx{`&NBH7hr(t_&PPm>x9H1mw&iJJ zoIJ@$c=HF|BTOV~sbiY_R>6?baI?H(8q4Gzsui1q)r6UWHb`wguX%uV^A`OBOq(Ye HeP#jx*kClb diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt new file mode 100644 index 00000000..dde990ab --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt @@ -0,0 +1,11 @@ +AutoFormat.RemoveSpansWithoutAttributes +TYPE: bool +VERSION: 4.0.1 +DEFAULT: false +--DESCRIPTION-- +

    + This directive causes span tags without any attributes + to be removed. It will also remove spans that had all attributes + removed during processing. +

    +--# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt new file mode 100644 index 00000000..f1f5c5f1 --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt @@ -0,0 +1,13 @@ +CSS.ForbiddenProperties +TYPE: lookup +VERSION: 4.2.0 +DEFAULT: array() +--DESCRIPTION-- +

    + This is the logical inverse of %CSS.AllowedProperties, and it will + override that directive or any other directive. If possible, + %CSS.AllowedProperties is recommended over this directive, + because it can sometimes be difficult to tell whether or not you've + forbidden all of the CSS properties you truly would like to disallow. +

    +--# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt new file mode 100644 index 00000000..d77f5360 --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt @@ -0,0 +1,11 @@ +Core.NormalizeNewlines +TYPE: bool +VERSION: 4.2.0 +DEFAULT: true +--DESCRIPTION-- +

    + Whether or not to normalize newlines to the operating + system default. When false, HTML Purifier + will attempt to preserve mixed newline files. +

    +--# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt new file mode 100644 index 00000000..3397d9f7 --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt @@ -0,0 +1,11 @@ +Core.RemoveProcessingInstructions +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +Instead of escaping processing instructions in the form <? ... +?>, remove it out-right. This may be useful if the HTML +you are validating contains XML processing instruction gunk, however, +it can also be user-unfriendly for people attempting to post PHP +snippets. +--# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt index 7fa6536b..321eaa2d 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -3,6 +3,11 @@ TYPE: bool VERSION: 3.1.0 DEFAULT: false --DESCRIPTION-- +

    + Warning: Deprecated in favor of %HTML.SafeObject and + %Output.FlashCompat (turn both on to allow YouTube videos and other + Flash content). +

    This directive enables YouTube video embedding in HTML Purifier. Check this document diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt index 3e231d2d..0b2c106d 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -5,11 +5,14 @@ DEFAULT: NULL --DESCRIPTION--

    - This is a convenience directive that rolls the functionality of - %HTML.AllowedElements and %HTML.AllowedAttributes into one directive. + This is a preferred convenience directive that combines + %HTML.AllowedElements and %HTML.AllowedAttributes. Specify elements and attributes that are allowed using: - element1[attr1|attr2],element2.... You can also use - newlines instead of commas to separate elements. + element1[attr1|attr2],element2.... For example, + if you would like to only allow paragraphs and links, specify + a[href],p. You can specify attributes that apply + to all elements using an asterisk, e.g. *[lang]. + You can also use newlines instead of commas to separate elements.

    Warning: diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt index 888d5581..1d3fa790 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -4,12 +4,17 @@ VERSION: 1.3.0 DEFAULT: NULL --DESCRIPTION--

    - If HTML Purifier's tag set is unsatisfactory for your needs, you - can overload it with your own list of tags to allow. Note that this - method is subtractive: it does its job by taking away from HTML Purifier - usual feature set, so you cannot add a tag that HTML Purifier never - supported in the first place (like embed, form or head). If you - change this, you probably also want to change %HTML.AllowedAttributes. + If HTML Purifier's tag set is unsatisfactory for your needs, you can + overload it with your own list of tags to allow. If you change + this, you probably also want to change %HTML.AllowedAttributes; see + also %HTML.Allowed which lets you set allowed elements and + attributes at the same time. +

    +

    + If you attempt to allow an element that HTML Purifier does not know + about, HTML Purifier will raise an error. You will need to manually + tell HTML Purifier about this element by using the + advanced customization features.

    Warning: If another directive conflicts with the diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt new file mode 100644 index 00000000..7878dc0b --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt @@ -0,0 +1,11 @@ +HTML.FlashAllowFullScreen +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +

    + Whether or not to permit embedded Flash content from + %HTML.SafeObject to expand to the full screen. Corresponds to + the allowFullScreen parameter. +

    +--# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt index f635a685..cdda09a4 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt @@ -7,8 +7,7 @@ DEFAULT: false Whether or not to permit embed tags in documents, with a number of extra security features added to prevent script execution. This is similar to what websites like MySpace do to embed tags. Embed is a proprietary - element and will cause your website to stop validating. You probably want - to enable this with %HTML.SafeObject. - Highly experimental. -

    + element and will cause your website to stop validating; you should + see if you can use %Output.FlashCompat with %HTML.SafeObject instead + first.

    --# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt index 32967b88..ceb342e2 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt @@ -6,9 +6,8 @@ DEFAULT: false

    Whether or not to permit object tags in documents, with a number of extra security features added to prevent script execution. This is similar to - what websites like MySpace do to object tags. You may also want to - enable %HTML.SafeEmbed for maximum interoperability with Internet Explorer, - although embed tags will cause your website to stop validating. - Highly experimental. + what websites like MySpace do to object tags. You should also enable + %Output.FlashCompat in order to generate Internet Explorer + compatibility code for your object tags.

    --# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt new file mode 100644 index 00000000..93398e85 --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt @@ -0,0 +1,11 @@ +Output.FlashCompat +TYPE: bool +VERSION: 4.1.0 +DEFAULT: false +--DESCRIPTION-- +

    + If true, HTML Purifier will generate Internet Explorer compatibility + code for all object code. This is highly recommended if you enable + %HTML.SafeObject. +

    +--# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt index 98fdfe92..666635a5 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -12,4 +12,6 @@ array ( --DESCRIPTION-- Whitelist that defines the schemes that a URI is allowed to have. This prevents XSS attacks from using pseudo-schemes like javascript or mocha. +There is also support for the data and file +URI schemes, but they are not enabled by default. --# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt index 51e6ea91..f891de49 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -1,12 +1,15 @@ URI.DisableResources TYPE: bool -VERSION: 1.3.0 +VERSION: 4.2.0 DEFAULT: false --DESCRIPTION-- -

    Disables embedding resources, essentially meaning no pictures. You can still link to them though. See %URI.DisableExternalResources for why this might be a good idea.

    +

    + Note: While this directive has been available since 1.3.0, + it didn't actually start doing anything until 4.2.0. +

    --# vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php index c4f5df97..cbd4e345 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php @@ -97,6 +97,13 @@ class HTMLPurifier_ElementDef */ public $autoclose = array(); + /** + * If a foreign element is found in this element, test if it is + * allowed by this sub-element; if it is, instead of closing the + * current element, place it inside this element. + */ + public $wrap; + /** * Whether or not this is a formatting element affected by the * "Active Formatting Elements" algorithm. diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php index aa3c17a0..9a9d9f96 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php @@ -7,13 +7,13 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter public function preFilter($html, $config, $context) { $pre_regex = '#]+>.+?'. - 'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?#s'; + 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s'; $pre_replace = '\1'; return preg_replace($pre_regex, $pre_replace, $html); } public function postFilter($html, $config, $context) { - $post_regex = '#([A-Za-z0-9\-_]+)#'; + $post_regex = '#((?:v|cp)/[A-Za-z0-9\-_=]+)#'; return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); } @@ -24,10 +24,10 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter protected function postFilterCallback($matches) { $url = $this->armorUrl($matches[1]); return ''. - ''. + 'data="http://www.youtube.com/'.$url.'">'. + ''. ''. diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php index 22e841c1..e5988b64 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php @@ -31,6 +31,17 @@ class HTMLPurifier_Generator */ private $_sortAttr; + /** + * Cache of %Output.FlashCompat + */ + private $_flashCompat; + + /** + * Stack for keeping track of object information when outputting IE + * compatibility code. + */ + private $_flashStack = array(); + /** * Configuration for the generator */ @@ -44,6 +55,7 @@ class HTMLPurifier_Generator $this->config = $config; $this->_scriptFix = $config->get('Output.CommentScriptContents'); $this->_sortAttr = $config->get('Output.SortAttr'); + $this->_flashCompat = $config->get('Output.FlashCompat'); $this->_def = $config->getHTMLDefinition(); $this->_xhtml = $this->_def->doctype->xml; } @@ -86,9 +98,11 @@ class HTMLPurifier_Generator } // Normalize newlines to system defined value - $nl = $this->config->get('Output.Newline'); - if ($nl === null) $nl = PHP_EOL; - if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + if ($this->config->get('Core.NormalizeNewlines')) { + $nl = $this->config->get('Output.Newline'); + if ($nl === null) $nl = PHP_EOL; + if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + } return $html; } @@ -104,12 +118,41 @@ class HTMLPurifier_Generator } elseif ($token instanceof HTMLPurifier_Token_Start) { $attr = $this->generateAttributes($token->attr, $token->name); + if ($this->_flashCompat) { + if ($token->name == "object") { + $flash = new stdclass(); + $flash->attr = $token->attr; + $flash->param = array(); + $this->_flashStack[] = $flash; + } + } return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; } elseif ($token instanceof HTMLPurifier_Token_End) { - return 'name . '>'; + $_extra = ''; + if ($this->_flashCompat) { + if ($token->name == "object" && !empty($this->_flashStack)) { + $flash = array_pop($this->_flashStack); + $compat_token = new HTMLPurifier_Token_Empty("embed"); + foreach ($flash->attr as $name => $val) { + if ($name == "classid") continue; + if ($name == "type") continue; + if ($name == "data") $name = "src"; + $compat_token->attr[$name] = $val; + } + foreach ($flash->param as $name => $val) { + if ($name == "movie") $name = "src"; + $compat_token->attr[$name] = $val; + } + $_extra = ""; + } + } + return $_extra . 'name . '>'; } elseif ($token instanceof HTMLPurifier_Token_Empty) { + if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) { + $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value']; + } $attr = $this->generateAttributes($token->attr, $token->name); return '<' . $token->name . ($attr ? ' ' : '') . $attr . ( $this->_xhtml ? ' /': '' ) //
    v.
    @@ -174,7 +217,10 @@ class HTMLPurifier_Generator * permissible for non-attribute output. * @return String escaped data. */ - public function escape($string, $quote = ENT_COMPAT) { + public function escape($string, $quote = null) { + // Workaround for APC bug on Mac Leopard reported by sidepodcast + // http://htmlpurifier.org/phorum/read.php?3,4823,4846 + if ($quote === null) $quote = ENT_COMPAT; return htmlspecialchars($string, $quote, 'UTF-8'); } diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php index 0195ce4c..f775604a 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php @@ -300,7 +300,12 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition unset($allowed_attributes_mutable[$key]); } } - if ($delete) unset($this->info[$tag]->attr[$attr]); + if ($delete) { + if ($this->info[$tag]->attr[$attr]->required) { + trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING); + } + unset($this->info[$tag]->attr[$attr]); + } } } // emit errors diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php index db2d5324..2911a69b 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php @@ -20,8 +20,10 @@ class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule public $content_sets = array('Flow' => 'List'); public function setup($config) { - $this->addElement('ol', 'List', 'Required: li', 'Common'); - $this->addElement('ul', 'List', 'Required: li', 'Common'); + $ol = $this->addElement('ol', 'List', 'Required: li', 'Common'); + $ol->wrap = "li"; + $ul = $this->addElement('ul', 'List', 'Required: li', 'Common'); + $ul->wrap = "li"; $this->addElement('dl', 'List', 'Required: dt | dd', 'Common'); $this->addElement('li', false, 'Flow', 'Common'); diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php index 1fd57145..7f602317 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -20,6 +20,7 @@ class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule 'height' => 'Pixels#' . $max, 'allowscriptaccess' => 'Enum#never', 'allownetworking' => 'Enum#internal', + 'flashvars' => 'Text', 'wmode' => 'Enum#window', 'name' => 'ID', ) diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php index 4378d2c6..d3de4f47 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php @@ -28,7 +28,10 @@ class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule 'type' => 'Enum#application/x-shockwave-flash', 'width' => 'Pixels#' . $max, 'height' => 'Pixels#' . $max, - 'data' => 'URI#embedded' + 'data' => 'URI#embedded', + 'classid' => 'Enum#clsid:d27cdb6e-ae6d-11cf-96b8-444553540000', + 'codebase' => new HTMLPurifier_AttrDef_Enum(array( + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0')), ) ); $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject(); diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php index f6aa6b03..d043aa72 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php @@ -15,6 +15,7 @@ class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_T $r['thead@background'] = new HTMLPurifier_AttrTransform_Background(); $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background(); $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['table@height'] = new HTMLPurifier_AttrTransform_Length('height'); return $r; } diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php index c5444dbe..72152d81 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php @@ -34,16 +34,21 @@ class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector // ---- // This is a degenerate case } else { - // State 1.2: PAR1 - // ---- + if (!$token->is_whitespace || $this->_isInline($current)) { + // State 1.2: PAR1 + // ---- - // State 1.3: PAR1\n\nPAR2 - // ------------ + // State 1.3: PAR1\n\nPAR2 + // ------------ - // State 1.4:
    PAR1\n\nPAR2 (see State 2) - // ------------ - $token = array($this->_pStart()); - $this->_splitText($text, $token); + // State 1.4:
    PAR1\n\nPAR2 (see State 2) + // ------------ + $token = array($this->_pStart()); + $this->_splitText($text, $token); + } else { + // State 1.5: \n
    + // -- + } } } else { // State 2:
    PAR1... (similar to 1.4) diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php new file mode 100644 index 00000000..509d5dc7 --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php @@ -0,0 +1,60 @@ +attrValidator = new HTMLPurifier_AttrValidator(); + $this->config = $config; + $this->context = $context; + return parent::prepare($config, $context); + } + + public function handleElement(&$token) { + if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) { + return; + } + + // We need to validate the attributes now since this doesn't normally + // happen until after MakeWellFormed. If all the attributes are removed + // the span needs to be removed too. + $this->attrValidator->validateToken($token, $this->config, $this->context); + $token->armor['ValidateAttributes'] = true; + + if (!empty($token->attr)) { + return; + } + + $nesting = 0; + $spanContentTokens = array(); + while ($this->forwardUntilEndToken($i, $current, $nesting)) {} + + if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') { + // Mark closing span tag for deletion + $current->markForDeletion = true; + // Delete open span tag + $token = false; + } + } + + public function handleEnd(&$token) { + if ($token->markForDeletion) { + $token = false; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php index 42d8fd40..fc01eebc 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php @@ -20,6 +20,9 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector protected $allowedParam = array( 'wmode' => true, 'movie' => true, + 'flashvars' => true, + 'src' => true, + 'allowFullScreen' => true, // if omitted, assume to be 'false' ); public function prepare($config, $context) { @@ -47,7 +50,8 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector // We need this fix because YouTube doesn't supply a data // attribute, which we need if a type is specified. This is // *very* Flash specific. - if (!isset($this->objectStack[$i]->attr['data']) && $token->attr['name'] == 'movie') { + if (!isset($this->objectStack[$i]->attr['data']) && + ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')) { $this->objectStack[$i]->attr['data'] = $token->attr['value']; } // Check if the parameter is the correct value but has not diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php index 5377e5a3..2c96e307 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php @@ -23,6 +23,7 @@ $messages = array( 'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', 'Lexer: Missing attribute key' => 'Attribute declaration has no key', 'Lexer: Missing end quote' => 'Attribute declaration has no end quote', +'Lexer: Extracted body' => 'Removed document metadata tags', 'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', 'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php index 9f20a412..6d6f4864 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php @@ -230,6 +230,17 @@ class HTMLPurifier_Lexer ); } + /** + * Special Internet Explorer conditional comments should be removed. + */ + protected static function removeIEConditional($string) { + return preg_replace( + '##si', // probably should generalize for all strings + '', + $string + ); + } + /** * Callback function for escapeCDATA() that does the work. * @@ -252,20 +263,32 @@ class HTMLPurifier_Lexer public function normalize($html, $config, $context) { // normalize newlines to \n - $html = str_replace("\r\n", "\n", $html); - $html = str_replace("\r", "\n", $html); + if ($config->get('Core.NormalizeNewlines')) { + $html = str_replace("\r\n", "\n", $html); + $html = str_replace("\r", "\n", $html); + } if ($config->get('HTML.Trusted')) { // escape convoluted CDATA $html = $this->escapeCommentedCDATA($html); } + $html = $this->removeIEConditional($html); + // escape CDATA $html = $this->escapeCDATA($html); // extract body from document if applicable if ($config->get('Core.ConvertDocumentToFragment')) { - $html = $this->extractBody($html); + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + $new_html = $this->extractBody($html); + if ($e && $new_html != $html) { + $e->send(E_WARNING, 'Lexer: Extracted body'); + } + $html = $new_html; } // expand entities that aren't the big five @@ -276,6 +299,11 @@ class HTMLPurifier_Lexer // represent non-SGML characters (horror, horror!) $html = HTMLPurifier_Encoder::cleanUTF8($html); + // if processing instructions are to removed, remove them now + if ($config->get('Core.RemoveProcessingInstructions')) { + $html = preg_replace('#<\?.+?\?>#s', '', $html); + } + return $html; } diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php index eb421b23..b7d9abfa 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php @@ -384,7 +384,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer } } if ($value === false) $value = ''; - return array($key => $value); + return array($key => $this->parseData($value)); } // setup loop environment diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php index 57173455..72c87635 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php @@ -26,13 +26,20 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer * Internal accumulator array for SAX parsers. */ protected $tokens = array(); + protected $last_token_was_empty; + + private $parent_handler; + private $stack = array(); public function tokenizeHTML($string, $config, $context) { $this->tokens = array(); + $this->last_token_was_empty = false; $string = $this->normalize($string, $config, $context); + $this->parent_handler = set_error_handler(array($this, 'muteStrictErrorHandler')); + $parser = new XML_HTMLSax3(); $parser->set_object($this); $parser->set_element_handler('openHandler','closeHandler'); @@ -44,6 +51,8 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer $parser->parse($string); + restore_error_handler(); + return $this->tokens; } @@ -58,9 +67,11 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer } if ($closed) { $this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs); + $this->last_token_was_empty = true; } else { $this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs); } + $this->stack[] = $name; return true; } @@ -71,10 +82,12 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer // HTMLSax3 seems to always send empty tags an extra close tag // check and ignore if you see it: // [TESTME] to make sure it doesn't overreach - if ($this->tokens[count($this->tokens)-1] instanceof HTMLPurifier_Token_Empty) { + if ($this->last_token_was_empty) { + $this->last_token_was_empty = false; return true; } $this->tokens[] = new HTMLPurifier_Token_End($name); + if (!empty($this->stack)) array_pop($this->stack); return true; } @@ -82,6 +95,7 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer * Data event handler, interface is defined by PEAR package. */ public function dataHandler(&$parser, $data) { + $this->last_token_was_empty = false; $this->tokens[] = new HTMLPurifier_Token_Text($data); return true; } @@ -91,7 +105,18 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer */ public function escapeHandler(&$parser, $data) { if (strpos($data, '--') === 0) { - $this->tokens[] = new HTMLPurifier_Token_Comment($data); + // remove trailing and leading double-dashes + $data = substr($data, 2); + if (strlen($data) >= 2 && substr($data, -2) == "--") { + $data = substr($data, 0, -2); + } + if (isset($this->stack[sizeof($this->stack) - 1]) && + $this->stack[sizeof($this->stack) - 1] == "style") { + $this->tokens[] = new HTMLPurifier_Token_Text($data); + } else { + $this->tokens[] = new HTMLPurifier_Token_Comment($data); + } + $this->last_token_was_empty = false; } // CDATA is handled elsewhere, but if it was handled here: //if (strpos($data, '[CDATA[') === 0) { @@ -101,6 +126,14 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer return true; } + /** + * An error handler that mutes strict errors + */ + public function muteStrictErrorHandler($errno, $errstr, $errfile=null, $errline=null, $errcontext=null) { + if ($errno == E_STRICT) return; + return call_user_func($this->parent_handler, $errno, $errstr, $errfile, $errline, $errcontext); + } + } // vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php index 0d20c0ce..b42965ef 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php @@ -125,8 +125,6 @@ class HTML5 { const EOF = 5; public function __construct($data) { - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", null, $data); $this->data = $data; $this->char = -1; @@ -3903,4 +3901,4 @@ class HTML5TreeConstructer { return $this->dom; } } - +?> diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php index c81b6b7b..03c92bd4 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php @@ -83,6 +83,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $this->injectors[] = $injector; } foreach ($custom_injectors as $injector) { + if (!$injector) continue; if (is_string($injector)) { $injector = "HTMLPurifier_Injector_$injector"; $injector = new $injector; @@ -164,6 +165,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $token = $tokens[$t]; //echo '
    '; printTokens($tokens, $t); printTokens($this->stack); + //flush(); // quick-check: if it's not a tag, no need to process if (empty($token->is_tag)) { @@ -219,6 +221,22 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $autoclose = false; } + if ($autoclose && $definition->info[$token->name]->wrap) { + // Check if an element can be wrapped by another + // element to make it valid in a context (for + // example,
        needs a
      • in between) + $wrapname = $definition->info[$token->name]->wrap; + $wrapdef = $definition->info[$wrapname]; + $elements = $wrapdef->child->getAllowedElements($config); + $parent_elements = $definition->info[$parent->name]->child->getAllowedElements($config); + if (isset($elements[$token->name]) && isset($parent_elements[$wrapname])) { + $newtoken = new HTMLPurifier_Token_Start($wrapname); + $this->insertBefore($newtoken); + $reprocess = true; + continue; + } + } + $carryover = false; if ($autoclose && $definition->info[$parent->name]->formatting) { $carryover = true; diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableResources.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableResources.php new file mode 100644 index 00000000..e69d970a --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableResources.php @@ -0,0 +1,11 @@ +get('EmbeddedURI', true); + } +} + +// vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/data.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/data.php new file mode 100644 index 00000000..bc4fb8cd --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/data.php @@ -0,0 +1,93 @@ + true, + 'image/gif' => true, + 'image/png' => true, + ); + + public function validate(&$uri, $config, $context) { + $result = explode(',', $uri->path, 2); + $is_base64 = false; + $charset = null; + $content_type = null; + if (count($result) == 2) { + list($metadata, $data) = $result; + // do some legwork on the metadata + $metas = explode(';', $metadata); + while(!empty($metas)) { + $cur = array_shift($metas); + if ($cur == 'base64') { + $is_base64 = true; + break; + } + if (substr($cur, 0, 8) == 'charset=') { + // doesn't match if there are arbitrary spaces, but + // whatever dude + if ($charset !== null) continue; // garbage + $charset = substr($cur, 8); // not used + } else { + if ($content_type !== null) continue; // garbage + $content_type = $cur; + } + } + } else { + $data = $result[0]; + } + if ($content_type !== null && empty($this->allowed_types[$content_type])) { + return false; + } + if ($charset !== null) { + // error; we don't allow plaintext stuff + $charset = null; + } + $data = rawurldecode($data); + if ($is_base64) { + $raw_data = base64_decode($data); + } else { + $raw_data = $data; + } + // XXX probably want to refactor this into a general mechanism + // for filtering arbitrary content types + $file = tempnam("/tmp", ""); + file_put_contents($file, $raw_data); + if (function_exists('exif_imagetype')) { + $image_code = exif_imagetype($file); + } elseif (function_exists('getimagesize')) { + set_error_handler(array($this, 'muteErrorHandler')); + $info = getimagesize($file); + restore_error_handler(); + if ($info == false) return false; + $image_code = $info[2]; + } else { + trigger_error("could not find exif_imagetype or getimagesize functions", E_USER_ERROR); + } + $real_content_type = image_type_to_mime_type($image_code); + if ($real_content_type != $content_type) { + // we're nice guys; if the content type is something else we + // support, change it over + if (empty($this->allowed_types[$real_content_type])) return false; + $content_type = $real_content_type; + } + // ok, it's kosher, rewrite what we need + $uri->userinfo = null; + $uri->host = null; + $uri->port = null; + $uri->fragment = null; + $uri->query = null; + $uri->path = "$content_type;base64," . base64_encode($raw_data); + return true; + } + + public function muteErrorHandler($errno, $errstr) {} + +} + diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/file.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/file.php new file mode 100644 index 00000000..66c30232 --- /dev/null +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/file.php @@ -0,0 +1,26 @@ +userinfo = null; + // file:// makes no provisions for accessing the resource + $uri->port = null; + // While it seems to work on Firefox, the querystring has + // no possible effect and is thus stripped. + $uri->query = null; + return true; + } + +} + +// vim: et sw=4 sts=4 diff --git a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php index e5998e5e..dea9f169 100644 --- a/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php +++ b/3.0/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php @@ -62,7 +62,7 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser foreach ($var as $keypair) { $c = explode(':', $keypair, 2); if (!isset($c[1])) continue; - $nvar[$c[0]] = $c[1]; + $nvar[trim($c[0])] = trim($c[1]); } $var = $nvar; } @@ -79,8 +79,15 @@ class HTMLPurifier_VarParser_Flexible extends HTMLPurifier_VarParser return $new; } else break; } + if ($type === self::ALIST) { + trigger_error("Array list did not have consecutive integer indexes", E_USER_WARNING); + return array_values($var); + } if ($type === self::LOOKUP) { foreach ($var as $key => $value) { + if ($value !== true) { + trigger_error("Lookup array has non-true value at key '$key'; maybe your input array was not indexed numerically", E_USER_WARNING); + } $var[$key] = true; } } From 426cd2a9082d78da4981f15a4d4052be806a1432 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 8 Jan 2011 18:40:34 -0800 Subject: [PATCH 223/300] Update HTMLPurifier to 4.2.0 and move the code from lib to vendor --- 3.1/modules/purifier/helpers/purifier.php | 2 +- .../HTMLPurifier/ConfigSchema/schema.ser | Bin 12999 -> 0 bytes .../schema/HTML.AllowedElements.txt | 18 ---- .../ConfigSchema/schema/HTML.SafeObject.txt | 14 --- .../HTMLPurifier/HTMLPurifier.auto.php | 0 .../HTMLPurifier/HTMLPurifier.autoload.php | 0 .../HTMLPurifier/HTMLPurifier.func.php | 0 .../HTMLPurifier/HTMLPurifier.includes.php | 6 +- .../HTMLPurifier/HTMLPurifier.kses.php | 0 .../HTMLPurifier/HTMLPurifier.path.php | 0 .../HTMLPurifier/HTMLPurifier.php | 6 +- .../HTMLPurifier.safe-includes.php | 4 + .../HTMLPurifier/AttrCollections.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef.php | 36 +++++++ .../HTMLPurifier/HTMLPurifier/AttrDef/CSS.php | 0 .../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 0 .../HTMLPurifier/AttrDef/CSS/Background.php | 0 .../AttrDef/CSS/BackgroundPosition.php | 21 ++-- .../HTMLPurifier/AttrDef/CSS/Border.php | 0 .../HTMLPurifier/AttrDef/CSS/Color.php | 0 .../HTMLPurifier/AttrDef/CSS/Composite.php | 0 .../AttrDef/CSS/DenyElementDecorator.php | 0 .../HTMLPurifier/AttrDef/CSS/Filter.php | 0 .../HTMLPurifier/AttrDef/CSS/Font.php | 0 .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 52 ++++------ .../AttrDef/CSS/ImportantDecorator.php | 0 .../HTMLPurifier/AttrDef/CSS/Length.php | 0 .../HTMLPurifier/AttrDef/CSS/ListStyle.php | 0 .../HTMLPurifier/AttrDef/CSS/Multiple.php | 0 .../HTMLPurifier/AttrDef/CSS/Number.php | 0 .../HTMLPurifier/AttrDef/CSS/Percentage.php | 0 .../AttrDef/CSS/TextDecoration.php | 0 .../HTMLPurifier/AttrDef/CSS/URI.php | 12 +-- .../HTMLPurifier/AttrDef/Enum.php | 0 .../HTMLPurifier/AttrDef/HTML/Bool.php | 0 .../HTMLPurifier/AttrDef/HTML/Class.php | 0 .../HTMLPurifier/AttrDef/HTML/Color.php | 0 .../HTMLPurifier/AttrDef/HTML/FrameTarget.php | 0 .../HTMLPurifier/AttrDef/HTML/ID.php | 0 .../HTMLPurifier/AttrDef/HTML/Length.php | 0 .../HTMLPurifier/AttrDef/HTML/LinkTypes.php | 0 .../HTMLPurifier/AttrDef/HTML/MultiLength.php | 0 .../HTMLPurifier/AttrDef/HTML/Nmtokens.php | 0 .../HTMLPurifier/AttrDef/HTML/Pixels.php | 0 .../HTMLPurifier/AttrDef/Integer.php | 0 .../HTMLPurifier/AttrDef/Lang.php | 0 .../HTMLPurifier/AttrDef/Switch.php | 0 .../HTMLPurifier/AttrDef/Text.php | 0 .../HTMLPurifier/HTMLPurifier/AttrDef/URI.php | 0 .../HTMLPurifier/AttrDef/URI/Email.php | 0 .../AttrDef/URI/Email/SimpleCheck.php | 0 .../HTMLPurifier/AttrDef/URI/Host.php | 0 .../HTMLPurifier/AttrDef/URI/IPv4.php | 0 .../HTMLPurifier/AttrDef/URI/IPv6.php | 0 .../HTMLPurifier/AttrTransform.php | 0 .../HTMLPurifier/AttrTransform/Background.php | 0 .../HTMLPurifier/AttrTransform/BdoDir.php | 0 .../HTMLPurifier/AttrTransform/BgColor.php | 0 .../HTMLPurifier/AttrTransform/BoolToCSS.php | 0 .../HTMLPurifier/AttrTransform/Border.php | 0 .../HTMLPurifier/AttrTransform/EnumToCSS.php | 0 .../AttrTransform/ImgRequired.php | 3 +- .../HTMLPurifier/AttrTransform/ImgSpace.php | 0 .../HTMLPurifier/AttrTransform/Input.php | 0 .../HTMLPurifier/AttrTransform/Lang.php | 0 .../HTMLPurifier/AttrTransform/Length.php | 0 .../HTMLPurifier/AttrTransform/Name.php | 0 .../HTMLPurifier/AttrTransform/NameSync.php | 0 .../HTMLPurifier/AttrTransform/SafeEmbed.php | 0 .../HTMLPurifier/AttrTransform/SafeObject.php | 0 .../HTMLPurifier/AttrTransform/SafeParam.php | 13 +++ .../AttrTransform/ScriptRequired.php | 0 .../HTMLPurifier/AttrTransform/Textarea.php | 0 .../HTMLPurifier/HTMLPurifier/AttrTypes.php | 0 .../HTMLPurifier/AttrValidator.php | 0 .../HTMLPurifier/HTMLPurifier/Bootstrap.php | 0 .../HTMLPurifier/CSSDefinition.php | 19 +++- .../HTMLPurifier/HTMLPurifier/ChildDef.php | 0 .../HTMLPurifier/ChildDef/Chameleon.php | 0 .../HTMLPurifier/ChildDef/Custom.php | 0 .../HTMLPurifier/ChildDef/Empty.php | 0 .../HTMLPurifier/ChildDef/Optional.php | 0 .../HTMLPurifier/ChildDef/Required.php | 0 .../ChildDef/StrictBlockquote.php | 0 .../HTMLPurifier/ChildDef/Table.php | 0 .../HTMLPurifier/HTMLPurifier/Config.php | 2 +- .../HTMLPurifier/ConfigSchema.php | 0 .../ConfigSchema/Builder/ConfigSchema.php | 0 .../HTMLPurifier/ConfigSchema/Builder/Xml.php | 0 .../HTMLPurifier/ConfigSchema/Exception.php | 0 .../HTMLPurifier/ConfigSchema/Interchange.php | 0 .../ConfigSchema/Interchange/Directive.php | 0 .../ConfigSchema/Interchange/Id.php | 0 .../ConfigSchema/InterchangeBuilder.php | 0 .../HTMLPurifier/ConfigSchema/Validator.php | 0 .../ConfigSchema/ValidatorAtom.php | 0 .../HTMLPurifier/ConfigSchema/schema.ser | Bin 0 -> 13701 bytes .../schema/Attr.AllowedClasses.txt | 0 .../schema/Attr.AllowedFrameTargets.txt | 0 .../ConfigSchema/schema/Attr.AllowedRel.txt | 0 .../ConfigSchema/schema/Attr.AllowedRev.txt | 0 .../schema/Attr.ClassUseCDATA.txt | 0 .../schema/Attr.DefaultImageAlt.txt | 0 .../schema/Attr.DefaultInvalidImage.txt | 0 .../schema/Attr.DefaultInvalidImageAlt.txt | 0 .../schema/Attr.DefaultTextDir.txt | 0 .../ConfigSchema/schema/Attr.EnableID.txt | 0 .../schema/Attr.ForbiddenClasses.txt | 0 .../ConfigSchema/schema/Attr.IDBlacklist.txt | 0 .../schema/Attr.IDBlacklistRegexp.txt | 0 .../ConfigSchema/schema/Attr.IDPrefix.txt | 0 .../schema/Attr.IDPrefixLocal.txt | 0 .../schema/AutoFormat.AutoParagraph.txt | 0 .../ConfigSchema/schema/AutoFormat.Custom.txt | 0 .../schema/AutoFormat.DisplayLinkURI.txt | 0 .../schema/AutoFormat.Linkify.txt | 0 .../AutoFormat.PurifierLinkify.DocURL.txt | 0 .../schema/AutoFormat.PurifierLinkify.txt | 0 ...rmat.RemoveEmpty.RemoveNbsp.Exceptions.txt | 0 .../AutoFormat.RemoveEmpty.RemoveNbsp.txt | 0 .../schema/AutoFormat.RemoveEmpty.txt | 0 ...utoFormat.RemoveSpansWithoutAttributes.txt | 11 +++ .../schema/CSS.AllowImportant.txt | 0 .../ConfigSchema/schema/CSS.AllowTricky.txt | 0 .../schema/CSS.AllowedProperties.txt | 0 .../ConfigSchema/schema/CSS.DefinitionRev.txt | 0 .../schema/CSS.ForbiddenProperties.txt | 13 +++ .../ConfigSchema/schema/CSS.MaxImgLength.txt | 0 .../ConfigSchema/schema/CSS.Proprietary.txt | 0 .../schema/Cache.DefinitionImpl.txt | 0 .../schema/Cache.SerializerPath.txt | 0 .../schema/Core.AggressivelyFixLt.txt | 0 .../schema/Core.CollectErrors.txt | 0 .../schema/Core.ColorKeywords.txt | 0 .../schema/Core.ConvertDocumentToFragment.txt | 0 .../Core.DirectLexLineNumberSyncInterval.txt | 0 .../ConfigSchema/schema/Core.Encoding.txt | 0 .../schema/Core.EscapeInvalidChildren.txt | 0 .../schema/Core.EscapeInvalidTags.txt | 0 .../schema/Core.EscapeNonASCIICharacters.txt | 0 .../schema/Core.HiddenElements.txt | 0 .../ConfigSchema/schema/Core.Language.txt | 0 .../ConfigSchema/schema/Core.LexerImpl.txt | 0 .../schema/Core.MaintainLineNumbers.txt | 0 .../schema/Core.NormalizeNewlines.txt | 11 +++ .../schema/Core.RemoveInvalidImg.txt | 0 .../Core.RemoveProcessingInstructions.txt | 11 +++ .../schema/Core.RemoveScriptContents.txt | 0 .../ConfigSchema/schema/Filter.Custom.txt | 0 .../Filter.ExtractStyleBlocks.Escaping.txt | 0 .../Filter.ExtractStyleBlocks.Scope.txt | 0 .../Filter.ExtractStyleBlocks.TidyImpl.txt | 0 .../schema/Filter.ExtractStyleBlocks.txt | 0 .../ConfigSchema/schema/Filter.YouTube.txt | 5 + .../ConfigSchema/schema/HTML.Allowed.txt | 11 ++- .../schema/HTML.AllowedAttributes.txt | 0 .../schema/HTML.AllowedElements.txt | 23 +++++ .../schema/HTML.AllowedModules.txt | 0 .../schema/HTML.Attr.Name.UseCDATA.txt | 0 .../ConfigSchema/schema/HTML.BlockWrapper.txt | 0 .../ConfigSchema/schema/HTML.CoreModules.txt | 0 .../schema/HTML.CustomDoctype.txt | 0 .../ConfigSchema/schema/HTML.DefinitionID.txt | 0 .../schema/HTML.DefinitionRev.txt | 0 .../ConfigSchema/schema/HTML.Doctype.txt | 0 .../schema/HTML.FlashAllowFullScreen.txt | 11 +++ .../schema/HTML.ForbiddenAttributes.txt | 0 .../schema/HTML.ForbiddenElements.txt | 0 .../ConfigSchema/schema/HTML.MaxImgLength.txt | 0 .../ConfigSchema/schema/HTML.Parent.txt | 0 .../ConfigSchema/schema/HTML.Proprietary.txt | 0 .../ConfigSchema/schema/HTML.SafeEmbed.txt | 7 +- .../ConfigSchema/schema/HTML.SafeObject.txt | 13 +++ .../ConfigSchema/schema/HTML.Strict.txt | 0 .../ConfigSchema/schema/HTML.TidyAdd.txt | 0 .../ConfigSchema/schema/HTML.TidyLevel.txt | 0 .../ConfigSchema/schema/HTML.TidyRemove.txt | 0 .../ConfigSchema/schema/HTML.Trusted.txt | 0 .../ConfigSchema/schema/HTML.XHTML.txt | 0 .../schema/Output.CommentScriptContents.txt | 0 .../schema/Output.FlashCompat.txt | 11 +++ .../ConfigSchema/schema/Output.Newline.txt | 0 .../ConfigSchema/schema/Output.SortAttr.txt | 0 .../ConfigSchema/schema/Output.TidyFormat.txt | 0 .../ConfigSchema/schema/Test.ForceNoIconv.txt | 0 .../schema/URI.AllowedSchemes.txt | 2 + .../ConfigSchema/schema/URI.Base.txt | 0 .../ConfigSchema/schema/URI.DefaultScheme.txt | 0 .../ConfigSchema/schema/URI.DefinitionID.txt | 0 .../ConfigSchema/schema/URI.DefinitionRev.txt | 0 .../ConfigSchema/schema/URI.Disable.txt | 0 .../schema/URI.DisableExternal.txt | 0 .../schema/URI.DisableExternalResources.txt | 0 .../schema/URI.DisableResources.txt | 7 +- .../ConfigSchema/schema/URI.Host.txt | 0 .../ConfigSchema/schema/URI.HostBlacklist.txt | 0 .../ConfigSchema/schema/URI.MakeAbsolute.txt | 0 .../ConfigSchema/schema/URI.Munge.txt | 0 .../schema/URI.MungeResources.txt | 0 .../schema/URI.MungeSecretKey.txt | 0 .../schema/URI.OverrideAllowedSchemes.txt | 0 .../HTMLPurifier/ConfigSchema/schema/info.ini | 0 .../HTMLPurifier/HTMLPurifier/ContentSets.php | 0 .../HTMLPurifier/HTMLPurifier/Context.php | 0 .../HTMLPurifier/HTMLPurifier/Definition.php | 0 .../HTMLPurifier/DefinitionCache.php | 0 .../DefinitionCache/Decorator.php | 0 .../DefinitionCache/Decorator/Cleanup.php | 0 .../DefinitionCache/Decorator/Memory.php | 0 .../DefinitionCache/Decorator/Template.php.in | 0 .../HTMLPurifier/DefinitionCache/Null.php | 0 .../DefinitionCache/Serializer.php | 0 .../DefinitionCache/Serializer/README | 0 .../HTMLPurifier/DefinitionCacheFactory.php | 0 .../HTMLPurifier/HTMLPurifier/Doctype.php | 0 .../HTMLPurifier/DoctypeRegistry.php | 0 .../HTMLPurifier/HTMLPurifier/ElementDef.php | 7 ++ .../HTMLPurifier/HTMLPurifier/Encoder.php | 0 .../HTMLPurifier/EntityLookup.php | 0 .../HTMLPurifier/EntityLookup/entities.ser | 0 .../HTMLPurifier/EntityParser.php | 0 .../HTMLPurifier/ErrorCollector.php | 0 .../HTMLPurifier/HTMLPurifier/ErrorStruct.php | 0 .../HTMLPurifier/HTMLPurifier/Exception.php | 0 .../HTMLPurifier/HTMLPurifier/Filter.php | 0 .../Filter/ExtractStyleBlocks.php | 0 .../HTMLPurifier/Filter/YouTube.php | 10 +- .../HTMLPurifier/HTMLPurifier/Generator.php | 56 ++++++++++- .../HTMLPurifier/HTMLDefinition.php | 7 +- .../HTMLPurifier/HTMLPurifier/HTMLModule.php | 0 .../HTMLPurifier/HTMLModule/Bdo.php | 0 .../HTMLModule/CommonAttributes.php | 0 .../HTMLPurifier/HTMLModule/Edit.php | 0 .../HTMLPurifier/HTMLModule/Forms.php | 0 .../HTMLPurifier/HTMLModule/Hypertext.php | 0 .../HTMLPurifier/HTMLModule/Image.php | 0 .../HTMLPurifier/HTMLModule/Legacy.php | 0 .../HTMLPurifier/HTMLModule/List.php | 6 +- .../HTMLPurifier/HTMLModule/Name.php | 0 .../HTMLModule/NonXMLCommonAttributes.php | 0 .../HTMLPurifier/HTMLModule/Object.php | 0 .../HTMLPurifier/HTMLModule/Presentation.php | 0 .../HTMLPurifier/HTMLModule/Proprietary.php | 0 .../HTMLPurifier/HTMLModule/Ruby.php | 0 .../HTMLPurifier/HTMLModule/SafeEmbed.php | 1 + .../HTMLPurifier/HTMLModule/SafeObject.php | 5 +- .../HTMLPurifier/HTMLModule/Scripting.php | 0 .../HTMLModule/StyleAttribute.php | 0 .../HTMLPurifier/HTMLModule/Tables.php | 0 .../HTMLPurifier/HTMLModule/Target.php | 0 .../HTMLPurifier/HTMLModule/Text.php | 0 .../HTMLPurifier/HTMLModule/Tidy.php | 0 .../HTMLPurifier/HTMLModule/Tidy/Name.php | 0 .../HTMLModule/Tidy/Proprietary.php | 1 + .../HTMLPurifier/HTMLModule/Tidy/Strict.php | 0 .../HTMLModule/Tidy/Transitional.php | 0 .../HTMLPurifier/HTMLModule/Tidy/XHTML.php | 0 .../HTMLModule/Tidy/XHTMLAndHTML4.php | 0 .../HTMLModule/XMLCommonAttributes.php | 0 .../HTMLPurifier/HTMLModuleManager.php | 0 .../HTMLPurifier/IDAccumulator.php | 0 .../HTMLPurifier/HTMLPurifier/Injector.php | 0 .../HTMLPurifier/Injector/AutoParagraph.php | 21 ++-- .../HTMLPurifier/Injector/DisplayLinkURI.php | 0 .../HTMLPurifier/Injector/Linkify.php | 0 .../HTMLPurifier/Injector/PurifierLinkify.php | 0 .../HTMLPurifier/Injector/RemoveEmpty.php | 0 .../Injector/RemoveSpansWithoutAttributes.php | 60 +++++++++++ .../HTMLPurifier/Injector/SafeObject.php | 6 +- .../HTMLPurifier/HTMLPurifier/Language.php | 0 .../Language/classes/en-x-test.php | 0 .../Language/messages/en-x-test.php | 0 .../Language/messages/en-x-testmini.php | 0 .../HTMLPurifier/Language/messages/en.php | 1 + .../HTMLPurifier/LanguageFactory.php | 0 .../HTMLPurifier/HTMLPurifier/Length.php | 0 .../HTMLPurifier/HTMLPurifier/Lexer.php | 34 ++++++- .../HTMLPurifier/Lexer/DOMLex.php | 0 .../HTMLPurifier/Lexer/DirectLex.php | 2 +- .../HTMLPurifier/Lexer/PEARSax3.php | 37 ++++++- .../HTMLPurifier/HTMLPurifier/Lexer/PH5P.php | 4 +- .../HTMLPurifier/PercentEncoder.php | 0 .../HTMLPurifier/HTMLPurifier/Printer.php | 0 .../HTMLPurifier/Printer/CSSDefinition.php | 0 .../HTMLPurifier/Printer/ConfigForm.css | 0 .../HTMLPurifier/Printer/ConfigForm.js | 0 .../HTMLPurifier/Printer/ConfigForm.php | 0 .../HTMLPurifier/Printer/HTMLDefinition.php | 0 .../HTMLPurifier/PropertyList.php | 0 .../HTMLPurifier/PropertyListIterator.php | 0 .../HTMLPurifier/HTMLPurifier/Strategy.php | 0 .../HTMLPurifier/Strategy/Composite.php | 0 .../HTMLPurifier/Strategy/Core.php | 0 .../HTMLPurifier/Strategy/FixNesting.php | 0 .../HTMLPurifier/Strategy/MakeWellFormed.php | 18 ++++ .../Strategy/RemoveForeignElements.php | 0 .../Strategy/ValidateAttributes.php | 0 .../HTMLPurifier/HTMLPurifier/StringHash.php | 0 .../HTMLPurifier/StringHashParser.php | 0 .../HTMLPurifier/TagTransform.php | 0 .../HTMLPurifier/TagTransform/Font.php | 0 .../HTMLPurifier/TagTransform/Simple.php | 0 .../HTMLPurifier/HTMLPurifier/Token.php | 0 .../HTMLPurifier/Token/Comment.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Empty.php | 0 .../HTMLPurifier/HTMLPurifier/Token/End.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Start.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Tag.php | 0 .../HTMLPurifier/HTMLPurifier/Token/Text.php | 0 .../HTMLPurifier/TokenFactory.php | 0 .../HTMLPurifier/HTMLPurifier/URI.php | 0 .../HTMLPurifier/URIDefinition.php | 0 .../HTMLPurifier/HTMLPurifier/URIFilter.php | 0 .../URIFilter/DisableExternal.php | 0 .../URIFilter/DisableExternalResources.php | 0 .../URIFilter/DisableResources.php | 11 +++ .../HTMLPurifier/URIFilter/HostBlacklist.php | 0 .../HTMLPurifier/URIFilter/MakeAbsolute.php | 0 .../HTMLPurifier/URIFilter/Munge.php | 0 .../HTMLPurifier/HTMLPurifier/URIParser.php | 0 .../HTMLPurifier/HTMLPurifier/URIScheme.php | 0 .../HTMLPurifier/URIScheme/data.php | 93 ++++++++++++++++++ .../HTMLPurifier/URIScheme/file.php | 26 +++++ .../HTMLPurifier/URIScheme/ftp.php | 0 .../HTMLPurifier/URIScheme/http.php | 0 .../HTMLPurifier/URIScheme/https.php | 0 .../HTMLPurifier/URIScheme/mailto.php | 0 .../HTMLPurifier/URIScheme/news.php | 0 .../HTMLPurifier/URIScheme/nntp.php | 0 .../HTMLPurifier/URISchemeRegistry.php | 0 .../HTMLPurifier/UnitConverter.php | 0 .../HTMLPurifier/HTMLPurifier/VarParser.php | 0 .../HTMLPurifier/VarParser/Flexible.php | 9 +- .../HTMLPurifier/VarParser/Native.php | 0 .../HTMLPurifier/VarParserException.php | 0 .../HTMLPurifier/HTMLPurifierLicense | 0 336 files changed, 622 insertions(+), 137 deletions(-) delete mode 100644 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser delete mode 100644 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt delete mode 100644 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.auto.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.autoload.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.func.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.includes.php (97%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.kses.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.path.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.php (98%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier.safe-includes.php (97%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrCollections.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef.php (71%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php (85%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php (51%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php (79%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/Text.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php (90%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php (74%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrTypes.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/AttrValidator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Bootstrap.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/CSSDefinition.php (95%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Required.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ChildDef/Table.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Config.php (99%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt (65%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt (54%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt (65%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt (74%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt (64%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ContentSets.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Context.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Definition.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Doctype.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ElementDef.php (96%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Encoder.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/EntityLookup.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/EntityParser.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ErrorCollector.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/ErrorStruct.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Exception.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Filter.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Filter/YouTube.php (74%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Generator.php (73%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLDefinition.php (97%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/List.php (85%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php (96%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php (83%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php (91%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/IDAccumulator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php (95%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/Linkify.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php (93%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Language/messages/en.php (98%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/LanguageFactory.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Length.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer.php (90%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php (99%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php (69%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php (99%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/PercentEncoder.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/PropertyList.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/PropertyListIterator.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/Composite.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/Core.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php (94%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/StringHash.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/StringHashParser.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TagTransform.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TagTransform/Font.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Comment.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Empty.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/End.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Start.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Tag.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/Token/Text.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/TokenFactory.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URI.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIDefinition.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIFilter/DisableResources.php rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIParser.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme.php (100%) create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/data.php create mode 100644 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/URIScheme/file.php rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/http.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/https.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/news.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/UnitConverter.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParser.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php (88%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParser/Native.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifier/VarParserException.php (100%) rename 3.1/modules/purifier/{lib => vendor}/HTMLPurifier/HTMLPurifierLicense (100%) diff --git a/3.1/modules/purifier/helpers/purifier.php b/3.1/modules/purifier/helpers/purifier.php index 97742b50..2af53180 100644 --- a/3.1/modules/purifier/helpers/purifier.php +++ b/3.1/modules/purifier/helpers/purifier.php @@ -21,7 +21,7 @@ class purifier { static function purify($dirty_html) { if (!isset(self::$_purifier)) { - require_once(MODPATH . "purifier/lib/HTMLPurifier/HTMLPurifier.auto.php"); + require_once(MODPATH . "purifier/vendor/HTMLPurifier/HTMLPurifier.auto.php"); $config = HTMLPurifier_Config::createDefault(); foreach (Kohana::config("purifier") as $category => $key_value) { foreach ($key_value as $key => $value) { diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser b/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser deleted file mode 100644 index bbf12f9c3e7392aa8143727d2485f6f9ad1f97e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12999 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 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt deleted file mode 100644 index 888d5581..00000000 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt +++ /dev/null @@ -1,18 +0,0 @@ -HTML.AllowedElements -TYPE: lookup/null -VERSION: 1.3.0 -DEFAULT: NULL ---DESCRIPTION-- -

        - If HTML Purifier's tag set is unsatisfactory for your needs, you - can overload it with your own list of tags to allow. Note that this - method is subtractive: it does its job by taking away from HTML Purifier - usual feature set, so you cannot add a tag that HTML Purifier never - supported in the first place (like embed, form or head). If you - change this, you probably also want to change %HTML.AllowedAttributes. -

        -

        - Warning: If another directive conflicts with the - elements here, that directive will win and override. -

        ---# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt deleted file mode 100644 index 32967b88..00000000 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt +++ /dev/null @@ -1,14 +0,0 @@ -HTML.SafeObject -TYPE: bool -VERSION: 3.1.1 -DEFAULT: false ---DESCRIPTION-- -

        - Whether or not to permit object tags in documents, with a number of extra - security features added to prevent script execution. This is similar to - what websites like MySpace do to object tags. You may also want to - enable %HTML.SafeEmbed for maximum interoperability with Internet Explorer, - although embed tags will cause your website to stop validating. - Highly experimental. -

        ---# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.auto.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.auto.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.auto.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.auto.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.autoload.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.autoload.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.autoload.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.autoload.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.func.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.func.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.func.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.func.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.includes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php similarity index 97% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.includes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php index e57f2ab3..08737c20 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.includes.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.includes.php @@ -7,7 +7,7 @@ * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS * FILE, changes will be overwritten the next time the script is run. * - * @version 4.0.0 + * @version 4.2.0 * * @warning * You must *not* include any other HTML Purifier files before this file, @@ -176,6 +176,7 @@ require 'HTMLPurifier/Injector/DisplayLinkURI.php'; require 'HTMLPurifier/Injector/Linkify.php'; require 'HTMLPurifier/Injector/PurifierLinkify.php'; require 'HTMLPurifier/Injector/RemoveEmpty.php'; +require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; require 'HTMLPurifier/Injector/SafeObject.php'; require 'HTMLPurifier/Lexer/DOMLex.php'; require 'HTMLPurifier/Lexer/DirectLex.php'; @@ -195,9 +196,12 @@ require 'HTMLPurifier/Token/Start.php'; require 'HTMLPurifier/Token/Text.php'; require 'HTMLPurifier/URIFilter/DisableExternal.php'; require 'HTMLPurifier/URIFilter/DisableExternalResources.php'; +require 'HTMLPurifier/URIFilter/DisableResources.php'; require 'HTMLPurifier/URIFilter/HostBlacklist.php'; require 'HTMLPurifier/URIFilter/MakeAbsolute.php'; require 'HTMLPurifier/URIFilter/Munge.php'; +require 'HTMLPurifier/URIScheme/data.php'; +require 'HTMLPurifier/URIScheme/file.php'; require 'HTMLPurifier/URIScheme/ftp.php'; require 'HTMLPurifier/URIScheme/http.php'; require 'HTMLPurifier/URIScheme/https.php'; diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.kses.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.kses.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.kses.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.kses.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.path.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.path.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.path.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.path.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php similarity index 98% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php index 71e90632..0430ad39 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.php @@ -19,7 +19,7 @@ */ /* - HTML Purifier 4.0.0 - Standards Compliant HTML Filtering + HTML Purifier 4.2.0 - Standards Compliant HTML Filtering Copyright (C) 2006-2008 Edward Z. Yang This library is free software; you can redistribute it and/or @@ -55,10 +55,10 @@ class HTMLPurifier { /** Version of HTML Purifier */ - public $version = '4.0.0'; + public $version = '4.2.0'; /** Constant with version of HTML Purifier */ - const VERSION = '4.0.0'; + const VERSION = '4.2.0'; /** Global configuration object */ public $config; diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.safe-includes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php similarity index 97% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.safe-includes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php index 5f0e1d8f..899a1f2e 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier.safe-includes.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier.safe-includes.php @@ -170,6 +170,7 @@ require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php'; require_once $__dir . '/HTMLPurifier/Injector/Linkify.php'; require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php'; require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php'; +require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php'; require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php'; require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php'; require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php'; @@ -189,9 +190,12 @@ require_once $__dir . '/HTMLPurifier/Token/Start.php'; require_once $__dir . '/HTMLPurifier/Token/Text.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php'; require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php'; +require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php'; require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php'; require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php'; require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/data.php'; +require_once $__dir . '/HTMLPurifier/URIScheme/file.php'; require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php'; require_once $__dir . '/HTMLPurifier/URIScheme/http.php'; require_once $__dir . '/HTMLPurifier/URIScheme/https.php'; diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrCollections.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrCollections.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrCollections.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrCollections.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php similarity index 71% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php index 7fac54e8..6f82201e 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef.php @@ -82,6 +82,42 @@ abstract class HTMLPurifier_AttrDef return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); } + /** + * Parses a possibly escaped CSS string and returns the "pure" + * version of it. + */ + protected function expandCSSEscape($string) { + // flexibly parse it + $ret = ''; + for ($i = 0, $c = strlen($string); $i < $c; $i++) { + if ($string[$i] === '\\') { + $i++; + if ($i >= $c) { + $ret .= '\\'; + break; + } + if (ctype_xdigit($string[$i])) { + $code = $string[$i]; + for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { + if (!ctype_xdigit($string[$i])) break; + $code .= $string[$i]; + } + // We have to be extremely careful when adding + // new characters, to make sure we're not breaking + // the encoding. + $char = HTMLPurifier_Encoder::unichr(hexdec($code)); + if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; + $ret .= $char; + if ($i < $c && trim($string[$i]) !== '') $i--; + continue; + } + if ($string[$i] === "\n") continue; + } + $ret .= $string[$i]; + } + return $ret; + } + } // vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php similarity index 85% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php index e067a754..665321e3 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php @@ -59,7 +59,8 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef $keywords = array(); $keywords['h'] = false; // left, right $keywords['v'] = false; // top, bottom - $keywords['c'] = false; // center + $keywords['ch'] = false; // center (first word) + $keywords['cv'] = false; // center (second word) $measures = array(); $i = 0; @@ -79,6 +80,13 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef $lbit = ctype_lower($bit) ? $bit : strtolower($bit); if (isset($lookup[$lbit])) { $status = $lookup[$lbit]; + if ($status == 'c') { + if ($i == 0) { + $status = 'ch'; + } else { + $status = 'cv'; + } + } $keywords[$status] = $lbit; $i++; } @@ -101,20 +109,19 @@ class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef if (!$i) return false; // no valid values were caught - $ret = array(); // first keyword if ($keywords['h']) $ret[] = $keywords['h']; - elseif (count($measures)) $ret[] = array_shift($measures); - elseif ($keywords['c']) { - $ret[] = $keywords['c']; - $keywords['c'] = false; // prevent re-use: center = center center + elseif ($keywords['ch']) { + $ret[] = $keywords['ch']; + $keywords['cv'] = false; // prevent re-use: center = center center } + elseif (count($measures)) $ret[] = array_shift($measures); if ($keywords['v']) $ret[] = $keywords['v']; + elseif ($keywords['cv']) $ret[] = $keywords['cv']; elseif (count($measures)) $ret[] = array_shift($measures); - elseif ($keywords['c']) $ret[] = $keywords['c']; if (empty($ret)) return false; return implode(' ', $ret); diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php similarity index 51% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php index 33435c76..f1ceec4a 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php @@ -34,37 +34,10 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef $quote = $font[0]; if ($font[$length - 1] !== $quote) continue; $font = substr($font, 1, $length - 2); - - $new_font = ''; - for ($i = 0, $c = strlen($font); $i < $c; $i++) { - if ($font[$i] === '\\') { - $i++; - if ($i >= $c) { - $new_font .= '\\'; - break; - } - if (ctype_xdigit($font[$i])) { - $code = $font[$i]; - for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { - if (!ctype_xdigit($font[$i])) break; - $code .= $font[$i]; - } - // We have to be extremely careful when adding - // new characters, to make sure we're not breaking - // the encoding. - $char = HTMLPurifier_Encoder::unichr(hexdec($code)); - if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; - $new_font .= $char; - if ($i < $c && trim($font[$i]) !== '') $i--; - continue; - } - if ($font[$i] === "\n") continue; - } - $new_font .= $font[$i]; - } - - $font = $new_font; } + + $font = $this->expandCSSEscape($font); + // $font is a pure representation of the font name if (ctype_alnum($font) && $font !== '') { @@ -73,12 +46,21 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef continue; } - // complicated font, requires quoting + // bugger out on whitespace. form feed (0C) really + // shouldn't show up regardless + $font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font); - // armor single quotes and new lines - $font = str_replace("\\", "\\\\", $font); - $font = str_replace("'", "\\'", $font); - $final .= "'$font', "; + // These ugly transforms don't pose a security + // risk (as \\ and \" might). We could try to be clever and + // use single-quote wrapping when there is a double quote + // present, but I have choosen not to implement that. + // (warning: this code relies on the selection of quotation + // mark below) + $font = str_replace('\\', '\\5C ', $font); + $font = str_replace('"', '\\22 ', $font); + + // complicated font, requires quoting + $final .= "\"$font\", "; // note that this will later get turned into " } $final = rtrim($final, ', '); if ($final === '') return false; diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php similarity index 79% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php index d09c87bc..98df033d 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php @@ -34,20 +34,16 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI $uri = substr($uri, 1, $new_length - 1); } - $keys = array( '(', ')', ',', ' ', '"', "'"); - $values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'"); - $uri = str_replace($values, $keys, $uri); + $uri = $this->expandCSSEscape($uri); $result = parent::validate($uri, $config, $context); if ($result === false) return false; - // escape necessary characters according to CSS spec - // except for the comma, none of these should appear in the - // URI at all - $result = str_replace($keys, $values, $result); + // extra sanity check; should have been done by URI + $result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result); - return "url($result)"; + return "url(\"$result\")"; } diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Text.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/Text.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php similarity index 90% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php index a1e5a83a..3d09eca3 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php @@ -24,7 +24,8 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform if ($src) { $alt = $config->get('Attr.DefaultImageAlt'); if ($alt === null) { - $attr['alt'] = basename($attr['src']); + // truncate if the alt is too long + $attr['alt'] = substr(basename($attr['src']),0,40); } else { $attr['alt'] = $alt; } diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php similarity index 74% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php index e677feae..d14390bc 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php @@ -33,12 +33,25 @@ class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform case 'allowNetworking': $attr['value'] = 'internal'; break; + case 'allowFullScreen': + if ($config->get('HTML.FlashAllowFullScreen')) { + $attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false'; + } else { + $attr['value'] = 'false'; + } + break; case 'wmode': $attr['value'] = 'window'; break; case 'movie': + case 'src': + $attr['name'] = "movie"; $attr['value'] = $this->uri->validate($attr['value'], $config, $context); break; + case 'flashvars': + // we're going to allow arbitrary inputs to the SWF, on + // the reasoning that it could only hack the SWF, not us. + break; // add other cases to support other param name/value pairs default: $attr['name'] = $attr['value'] = null; diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTypes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTypes.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrTypes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrTypes.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrValidator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrValidator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/AttrValidator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/AttrValidator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Bootstrap.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Bootstrap.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Bootstrap.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Bootstrap.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/CSSDefinition.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php similarity index 95% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/CSSDefinition.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php index 17bf9931..09afc1f1 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/CSSDefinition.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/CSSDefinition.php @@ -272,20 +272,29 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition // setup allowed elements $support = "(for information on implementing this, see the ". "support forums) "; - $allowed_attributes = $config->get('CSS.AllowedProperties'); - if ($allowed_attributes !== null) { + $allowed_properties = $config->get('CSS.AllowedProperties'); + if ($allowed_properties !== null) { foreach ($this->info as $name => $d) { - if(!isset($allowed_attributes[$name])) unset($this->info[$name]); - unset($allowed_attributes[$name]); + if(!isset($allowed_properties[$name])) unset($this->info[$name]); + unset($allowed_properties[$name]); } // emit errors - foreach ($allowed_attributes as $name => $d) { + foreach ($allowed_properties as $name => $d) { // :TODO: Is this htmlspecialchars() call really necessary? $name = htmlspecialchars($name); trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); } } + $forbidden_properties = $config->get('CSS.ForbiddenProperties'); + if ($forbidden_properties !== null) { + foreach ($this->info as $name => $d) { + if (isset($forbidden_properties[$name])) { + unset($this->info[$name]); + } + } + } + } } diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Required.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Required.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Table.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ChildDef/Table.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Config.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php similarity index 99% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Config.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php index 28529e7f..ada1b701 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Config.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Config.php @@ -20,7 +20,7 @@ class HTMLPurifier_Config /** * HTML Purifier's version */ - public $version = '4.0.0'; + public $version = '4.2.0'; /** * Bool indicator whether or not to automatically finalize diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser new file mode 100644 index 0000000000000000000000000000000000000000..978089c6291e7d828f6233ac632cedd8a98fdfd7 GIT binary patch literal 13701 zcmeHO+in{<5}i*8JlK~7B1m?!lTn@=*+PW%4VHtMeHyeRTIRadt?i3rZ;*fAbBaYa z)oRI^hh1O+IS4Fu6^q5Xk*q$x9v;6R9(}&Mytt_gdv8tg$0*P4?Q%MQFstNfcryG} z4nGW!7Un*w)2clBm<$K~cYHedb$E1IRYmVKP4lm2F-nuNG~l@USPqZhZ4$>tvNCr` zu{2IbfB5Y?L}2n*#H~p=!7k!cM1)Q9n$g+m-KoyqSK66Vda_EE<}|HZnt5vW zl%#ecOh?chgZbqVqvmi)nJytDa+*SQ<}^lQ(IzXo_c(|O}#Zs^SpMe4>M2B zZVGd6pQ9xYyiGmrBA+L4NMjQ^wXSj~w@RuWKi(uovMiGIgI~dbc58S>by?-BNI!bK zA)ncDohC0AHhcVXI|(wVNW(;`y?^o46B@6f`oLAL-dR5Xa(kin{`oJ6N0^m1+d0Gw zez&>2TeHfa%=v0vz4YAYt6913oj=dby0UpDxp2%(fX%A~r3^S1zKAsj9}(d#WxHj1 z1;lxcnh~?-IAPAUIi~9*D}S@qLta;mDm$wy=AjEIdUZ6Nx-6Zn)_GAS86rlh@JFED z6?Xm@?95mUB(u0F^0g@{>oc5=CK+*VGfRWq8YHN7Mu3k<3}GCP_OUvESIBgkJWp23 z3zIErxN?ZcgX4aGEwRZGK_&_)tf`WsQ4py*N>HdG9k4)&*a2u$Q&^<-zm4SFao}#A z-z1z->J{8J$_vvwT`mh#miEb{FJt?BA(o)p=3m#io^&niqdJFJJRw zArS&QAw;mIRZ`@6#s$K1KY!@+N5ix*2p{)+Joe8qBrlT8l@$&dk5AvbAt1dNR^4V4 z$MGA?m8a0&Q$F-L6Te~5x-QmfMCya6jQ92Yp|pt~MsedYzd_*F2U~?vWBDNk>5|%2 zAaFp?0Y)@ot(?YDFeFQ!;6b-oF4buO!Yr*LS$=vDl7H1fFFtm9flwJ&o@g8ja-yK{ zjD<0tBv_!d>642G4jT3OPuGGn78Ii_N@E5yZk%T`46RUK>Xpf=yBvGklAoAMjF1yp zSU6Gd5g-@l88yjV)vK8)rZ3ril2xX_X2T?5v1ct7{_|{}FKo7?BGA6B?%#gkXO=C; zP|lOJ(Y*}82L-#!9x=WTJIFC^^A~{!ib~+tRCZh)w?>Te|jNH70@w4nb z=c(cPB#fodL7#G7*mYPkv@NR_h7?V}9o?_#0+Czmo(Kh9Fbv={kq_IL!sV(zEy(lB z#AX%#H-^LH8~J&~y@yGCWxl2`ujnpym*t9na%EGEg~Aft<5hDEC@6C#dbXToWmVL3 zt?6o1tSpi?l~vc`S2XgJR{E6Y^T)C$eiC3yuxX**PQBfuR_WSi`eluoI0=}F;8H>wfshW3IjbC zq$jAi-^w8-pNgC7Lc>)m7|&-uLa5&hH@WB;EOa3!EZyB<7v#_HmlvaawL&6o?68B%$D>2ddAi02NBOQIh{7y}M93I1;*LAh7D;zdfEWz#i1}l*CoIbAC*`GiAj?-P~N|lX;%OJ!)*I zf)l(i7jk`MY9}z#H%YXIszQ=F#i0SehT2U-CMc!3XFxHyZ`iSrEZh8QzUp9PjZXb3 zDWg=OGI|Ab%Fss;lUbk(e#Iius)L$|sqxy7EcKZIFpWd6&tfIh~bxii?5QNj1K# z<%H?Wh;Z}K*4zxXK%E-Yi3-PDrv?uyq+R2HRM-VB+tLvTQ9!S=ydet~?oNK=uC3SG z{Rh1%;7*7|y8Z(e`O{y2S|k;Yfrs{kYh}QiNRmVQ;n03Kv>y)bhqyU$Xg>^(4(*3S z`{B@jKxYC+%|rV^&&s^sM=g!eAmG{e(0(|y9~c{l_5*SOH$CkF9NG`AYj9{kus!jA z-hM#+M;l?S4MtXdEC^5Le22(A0woX~BAeZ#4~0I1dvh0F=(k}GcKG%KcMc3;56!o} zHjxf?NS;od!!DA%M3qrcPb9Z64w(2CPK;)ekhRU#z#PCJZ$!Lio*{xTgcr5wt!xO1 zcm(F)-sc7*sHlKyxTX!u%7H(Qa25R~Hgs)kHx@G_t{+6J3I{qDf`oA0Kzu7I>6M{N z#BB%hBJyMc7(HyE7%2_v>!02<&& zGtk3Cu-2_Q5cFu{M7$ozbIj$H|QF@u{*#G%7N^O z)b5_K4Q|l5^@=vg1y|6zKnn~7zs1VroxlZf$wuwh(1BghHQ=q1h;g|eV;39) zD^A3)3%j7wnEnhutt2h%f@(#_=)3>T4P!sbpaCy5n(f3HG|aC=BO-aHkO(i)9g*0( z++eraY_~lAH)_C#yVsy74lN%XnQUT5>Yv#ZWN$4*}L zZ!_ij3wiJ(o9>EC~xndw(VwMJ8 zp$t5p#|)vhh__{&lr`$LG$gm-LKmoLfE+OxEP4R|*^RCw) zytxC^vJ@Drdu=be>#`k3gB6j=58VW`@n+EL?;PE5Y>tM%JZa!mMU^hodAv zkGGCK_UbdS1&5Id6M??I7CV0rE4uqCP6DyVSLd>--SeZtu3AV;7hryuWb5#JVF#W+Az%ML9} klV!Z literal 0 HcmV?d00001 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt new file mode 100644 index 00000000..dde990ab --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveSpansWithoutAttributes.txt @@ -0,0 +1,11 @@ +AutoFormat.RemoveSpansWithoutAttributes +TYPE: bool +VERSION: 4.0.1 +DEFAULT: false +--DESCRIPTION-- +

        + This directive causes span tags without any attributes + to be removed. It will also remove spans that had all attributes + removed during processing. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt new file mode 100644 index 00000000..f1f5c5f1 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.ForbiddenProperties.txt @@ -0,0 +1,13 @@ +CSS.ForbiddenProperties +TYPE: lookup +VERSION: 4.2.0 +DEFAULT: array() +--DESCRIPTION-- +

        + This is the logical inverse of %CSS.AllowedProperties, and it will + override that directive or any other directive. If possible, + %CSS.AllowedProperties is recommended over this directive, + because it can sometimes be difficult to tell whether or not you've + forbidden all of the CSS properties you truly would like to disallow. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt new file mode 100644 index 00000000..d77f5360 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.NormalizeNewlines.txt @@ -0,0 +1,11 @@ +Core.NormalizeNewlines +TYPE: bool +VERSION: 4.2.0 +DEFAULT: true +--DESCRIPTION-- +

        + Whether or not to normalize newlines to the operating + system default. When false, HTML Purifier + will attempt to preserve mixed newline files. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt new file mode 100644 index 00000000..3397d9f7 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveProcessingInstructions.txt @@ -0,0 +1,11 @@ +Core.RemoveProcessingInstructions +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +Instead of escaping processing instructions in the form <? ... +?>, remove it out-right. This may be useful if the HTML +you are validating contains XML processing instruction gunk, however, +it can also be user-unfriendly for people attempting to post PHP +snippets. +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt similarity index 65% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt index 7fa6536b..321eaa2d 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt @@ -3,6 +3,11 @@ TYPE: bool VERSION: 3.1.0 DEFAULT: false --DESCRIPTION-- +

        + Warning: Deprecated in favor of %HTML.SafeObject and + %Output.FlashCompat (turn both on to allow YouTube videos and other + Flash content). +

        This directive enables YouTube video embedding in HTML Purifier. Check this document diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt similarity index 54% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt index 3e231d2d..0b2c106d 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt @@ -5,11 +5,14 @@ DEFAULT: NULL --DESCRIPTION--

        - This is a convenience directive that rolls the functionality of - %HTML.AllowedElements and %HTML.AllowedAttributes into one directive. + This is a preferred convenience directive that combines + %HTML.AllowedElements and %HTML.AllowedAttributes. Specify elements and attributes that are allowed using: - element1[attr1|attr2],element2.... You can also use - newlines instead of commas to separate elements. + element1[attr1|attr2],element2.... For example, + if you would like to only allow paragraphs and links, specify + a[href],p. You can specify attributes that apply + to all elements using an asterisk, e.g. *[lang]. + You can also use newlines instead of commas to separate elements.

        Warning: diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt new file mode 100644 index 00000000..1d3fa790 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt @@ -0,0 +1,23 @@ +HTML.AllowedElements +TYPE: lookup/null +VERSION: 1.3.0 +DEFAULT: NULL +--DESCRIPTION-- +

        + If HTML Purifier's tag set is unsatisfactory for your needs, you can + overload it with your own list of tags to allow. If you change + this, you probably also want to change %HTML.AllowedAttributes; see + also %HTML.Allowed which lets you set allowed elements and + attributes at the same time. +

        +

        + If you attempt to allow an element that HTML Purifier does not know + about, HTML Purifier will raise an error. You will need to manually + tell HTML Purifier about this element by using the + advanced customization features. +

        +

        + Warning: If another directive conflicts with the + elements here, that directive will win and override. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt new file mode 100644 index 00000000..7878dc0b --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.FlashAllowFullScreen.txt @@ -0,0 +1,11 @@ +HTML.FlashAllowFullScreen +TYPE: bool +VERSION: 4.2.0 +DEFAULT: false +--DESCRIPTION-- +

        + Whether or not to permit embedded Flash content from + %HTML.SafeObject to expand to the full screen. Corresponds to + the allowFullScreen parameter. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt similarity index 65% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt index f635a685..cdda09a4 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt @@ -7,8 +7,7 @@ DEFAULT: false Whether or not to permit embed tags in documents, with a number of extra security features added to prevent script execution. This is similar to what websites like MySpace do to embed tags. Embed is a proprietary - element and will cause your website to stop validating. You probably want - to enable this with %HTML.SafeObject. - Highly experimental. -

        + element and will cause your website to stop validating; you should + see if you can use %Output.FlashCompat with %HTML.SafeObject instead + first.

        --# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt new file mode 100644 index 00000000..ceb342e2 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt @@ -0,0 +1,13 @@ +HTML.SafeObject +TYPE: bool +VERSION: 3.1.1 +DEFAULT: false +--DESCRIPTION-- +

        + Whether or not to permit object tags in documents, with a number of extra + security features added to prevent script execution. This is similar to + what websites like MySpace do to object tags. You should also enable + %Output.FlashCompat in order to generate Internet Explorer + compatibility code for your object tags. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt new file mode 100644 index 00000000..93398e85 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.FlashCompat.txt @@ -0,0 +1,11 @@ +Output.FlashCompat +TYPE: bool +VERSION: 4.1.0 +DEFAULT: false +--DESCRIPTION-- +

        + If true, HTML Purifier will generate Internet Explorer compatibility + code for all object code. This is highly recommended if you enable + %HTML.SafeObject. +

        +--# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt similarity index 74% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt index 98fdfe92..666635a5 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt @@ -12,4 +12,6 @@ array ( --DESCRIPTION-- Whitelist that defines the schemes that a URI is allowed to have. This prevents XSS attacks from using pseudo-schemes like javascript or mocha. +There is also support for the data and file +URI schemes, but they are not enabled by default. --# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt similarity index 64% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt index 51e6ea91..f891de49 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt @@ -1,12 +1,15 @@ URI.DisableResources TYPE: bool -VERSION: 1.3.0 +VERSION: 4.2.0 DEFAULT: false --DESCRIPTION-- -

        Disables embedding resources, essentially meaning no pictures. You can still link to them though. See %URI.DisableExternalResources for why this might be a good idea.

        +

        + Note: While this directive has been available since 1.3.0, + it didn't actually start doing anything until 4.2.0. +

        --# vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ContentSets.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ContentSets.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ContentSets.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ContentSets.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Context.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Context.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Context.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Context.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Definition.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Definition.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Definition.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Definition.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Doctype.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Doctype.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Doctype.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Doctype.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ElementDef.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php similarity index 96% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ElementDef.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php index c4f5df97..cbd4e345 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ElementDef.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ElementDef.php @@ -97,6 +97,13 @@ class HTMLPurifier_ElementDef */ public $autoclose = array(); + /** + * If a foreign element is found in this element, test if it is + * allowed by this sub-element; if it is, instead of closing the + * current element, place it inside this element. + */ + public $wrap; + /** * Whether or not this is a formatting element affected by the * "Active Formatting Elements" algorithm. diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Encoder.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Encoder.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Encoder.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Encoder.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityParser.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityParser.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/EntityParser.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/EntityParser.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorCollector.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorCollector.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorCollector.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorCollector.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorStruct.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorStruct.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/ErrorStruct.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/ErrorStruct.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Exception.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Exception.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Exception.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Exception.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php similarity index 74% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php index aa3c17a0..9a9d9f96 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Filter/YouTube.php @@ -7,13 +7,13 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter public function preFilter($html, $config, $context) { $pre_regex = '#]+>.+?'. - 'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?
    #s'; + 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?#s'; $pre_replace = '\1'; return preg_replace($pre_regex, $pre_replace, $html); } public function postFilter($html, $config, $context) { - $post_regex = '#([A-Za-z0-9\-_]+)#'; + $post_regex = '#((?:v|cp)/[A-Za-z0-9\-_=]+)#'; return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); } @@ -24,10 +24,10 @@ class HTMLPurifier_Filter_YouTube extends HTMLPurifier_Filter protected function postFilterCallback($matches) { $url = $this->armorUrl($matches[1]); return ''. - ''. + 'data="http://www.youtube.com/'.$url.'">'. + ''. ''. diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Generator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php similarity index 73% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Generator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php index 22e841c1..e5988b64 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Generator.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Generator.php @@ -31,6 +31,17 @@ class HTMLPurifier_Generator */ private $_sortAttr; + /** + * Cache of %Output.FlashCompat + */ + private $_flashCompat; + + /** + * Stack for keeping track of object information when outputting IE + * compatibility code. + */ + private $_flashStack = array(); + /** * Configuration for the generator */ @@ -44,6 +55,7 @@ class HTMLPurifier_Generator $this->config = $config; $this->_scriptFix = $config->get('Output.CommentScriptContents'); $this->_sortAttr = $config->get('Output.SortAttr'); + $this->_flashCompat = $config->get('Output.FlashCompat'); $this->_def = $config->getHTMLDefinition(); $this->_xhtml = $this->_def->doctype->xml; } @@ -86,9 +98,11 @@ class HTMLPurifier_Generator } // Normalize newlines to system defined value - $nl = $this->config->get('Output.Newline'); - if ($nl === null) $nl = PHP_EOL; - if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + if ($this->config->get('Core.NormalizeNewlines')) { + $nl = $this->config->get('Output.Newline'); + if ($nl === null) $nl = PHP_EOL; + if ($nl !== "\n") $html = str_replace("\n", $nl, $html); + } return $html; } @@ -104,12 +118,41 @@ class HTMLPurifier_Generator } elseif ($token instanceof HTMLPurifier_Token_Start) { $attr = $this->generateAttributes($token->attr, $token->name); + if ($this->_flashCompat) { + if ($token->name == "object") { + $flash = new stdclass(); + $flash->attr = $token->attr; + $flash->param = array(); + $this->_flashStack[] = $flash; + } + } return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; } elseif ($token instanceof HTMLPurifier_Token_End) { - return 'name . '>'; + $_extra = ''; + if ($this->_flashCompat) { + if ($token->name == "object" && !empty($this->_flashStack)) { + $flash = array_pop($this->_flashStack); + $compat_token = new HTMLPurifier_Token_Empty("embed"); + foreach ($flash->attr as $name => $val) { + if ($name == "classid") continue; + if ($name == "type") continue; + if ($name == "data") $name = "src"; + $compat_token->attr[$name] = $val; + } + foreach ($flash->param as $name => $val) { + if ($name == "movie") $name = "src"; + $compat_token->attr[$name] = $val; + } + $_extra = ""; + } + } + return $_extra . 'name . '>'; } elseif ($token instanceof HTMLPurifier_Token_Empty) { + if ($this->_flashCompat && $token->name == "param" && !empty($this->_flashStack)) { + $this->_flashStack[count($this->_flashStack)-1]->param[$token->attr['name']] = $token->attr['value']; + } $attr = $this->generateAttributes($token->attr, $token->name); return '<' . $token->name . ($attr ? ' ' : '') . $attr . ( $this->_xhtml ? ' /': '' ) //
    v.
    @@ -174,7 +217,10 @@ class HTMLPurifier_Generator * permissible for non-attribute output. * @return String escaped data. */ - public function escape($string, $quote = ENT_COMPAT) { + public function escape($string, $quote = null) { + // Workaround for APC bug on Mac Leopard reported by sidepodcast + // http://htmlpurifier.org/phorum/read.php?3,4823,4846 + if ($quote === null) $quote = ENT_COMPAT; return htmlspecialchars($string, $quote, 'UTF-8'); } diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php similarity index 97% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php index 0195ce4c..f775604a 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLDefinition.php @@ -300,7 +300,12 @@ class HTMLPurifier_HTMLDefinition extends HTMLPurifier_Definition unset($allowed_attributes_mutable[$key]); } } - if ($delete) unset($this->info[$tag]->attr[$attr]); + if ($delete) { + if ($this->info[$tag]->attr[$attr]->required) { + trigger_error("Required attribute '$attr' in element '$tag' was not allowed, which means '$tag' will not be allowed either", E_USER_WARNING); + } + unset($this->info[$tag]->attr[$attr]); + } } } // emit errors diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php similarity index 85% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php index db2d5324..2911a69b 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/List.php @@ -20,8 +20,10 @@ class HTMLPurifier_HTMLModule_List extends HTMLPurifier_HTMLModule public $content_sets = array('Flow' => 'List'); public function setup($config) { - $this->addElement('ol', 'List', 'Required: li', 'Common'); - $this->addElement('ul', 'List', 'Required: li', 'Common'); + $ol = $this->addElement('ol', 'List', 'Required: li', 'Common'); + $ol->wrap = "li"; + $ul = $this->addElement('ul', 'List', 'Required: li', 'Common'); + $ul->wrap = "li"; $this->addElement('dl', 'List', 'Required: dt | dd', 'Common'); $this->addElement('li', false, 'Flow', 'Common'); diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php similarity index 96% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php index 1fd57145..7f602317 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php @@ -20,6 +20,7 @@ class HTMLPurifier_HTMLModule_SafeEmbed extends HTMLPurifier_HTMLModule 'height' => 'Pixels#' . $max, 'allowscriptaccess' => 'Enum#never', 'allownetworking' => 'Enum#internal', + 'flashvars' => 'Text', 'wmode' => 'Enum#window', 'name' => 'ID', ) diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php similarity index 83% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php index 4378d2c6..d3de4f47 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php @@ -28,7 +28,10 @@ class HTMLPurifier_HTMLModule_SafeObject extends HTMLPurifier_HTMLModule 'type' => 'Enum#application/x-shockwave-flash', 'width' => 'Pixels#' . $max, 'height' => 'Pixels#' . $max, - 'data' => 'URI#embedded' + 'data' => 'URI#embedded', + 'classid' => 'Enum#clsid:d27cdb6e-ae6d-11cf-96b8-444553540000', + 'codebase' => new HTMLPurifier_AttrDef_Enum(array( + 'http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0')), ) ); $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject(); diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php similarity index 91% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php index f6aa6b03..d043aa72 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php @@ -15,6 +15,7 @@ class HTMLPurifier_HTMLModule_Tidy_Proprietary extends HTMLPurifier_HTMLModule_T $r['thead@background'] = new HTMLPurifier_AttrTransform_Background(); $r['tfoot@background'] = new HTMLPurifier_AttrTransform_Background(); $r['tbody@background'] = new HTMLPurifier_AttrTransform_Background(); + $r['table@height'] = new HTMLPurifier_AttrTransform_Length('height'); return $r; } diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/IDAccumulator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/IDAccumulator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/IDAccumulator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/IDAccumulator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php similarity index 95% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php index c5444dbe..72152d81 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php @@ -34,16 +34,21 @@ class HTMLPurifier_Injector_AutoParagraph extends HTMLPurifier_Injector // ---- // This is a degenerate case } else { - // State 1.2: PAR1 - // ---- + if (!$token->is_whitespace || $this->_isInline($current)) { + // State 1.2: PAR1 + // ---- - // State 1.3: PAR1\n\nPAR2 - // ------------ + // State 1.3: PAR1\n\nPAR2 + // ------------ - // State 1.4:
    PAR1\n\nPAR2 (see State 2) - // ------------ - $token = array($this->_pStart()); - $this->_splitText($text, $token); + // State 1.4:
    PAR1\n\nPAR2 (see State 2) + // ------------ + $token = array($this->_pStart()); + $this->_splitText($text, $token); + } else { + // State 1.5: \n
    + // -- + } } } else { // State 2:
    PAR1... (similar to 1.4) diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/Linkify.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/Linkify.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php diff --git a/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php new file mode 100644 index 00000000..509d5dc7 --- /dev/null +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php @@ -0,0 +1,60 @@ +attrValidator = new HTMLPurifier_AttrValidator(); + $this->config = $config; + $this->context = $context; + return parent::prepare($config, $context); + } + + public function handleElement(&$token) { + if ($token->name !== 'span' || !$token instanceof HTMLPurifier_Token_Start) { + return; + } + + // We need to validate the attributes now since this doesn't normally + // happen until after MakeWellFormed. If all the attributes are removed + // the span needs to be removed too. + $this->attrValidator->validateToken($token, $this->config, $this->context); + $token->armor['ValidateAttributes'] = true; + + if (!empty($token->attr)) { + return; + } + + $nesting = 0; + $spanContentTokens = array(); + while ($this->forwardUntilEndToken($i, $current, $nesting)) {} + + if ($current instanceof HTMLPurifier_Token_End && $current->name === 'span') { + // Mark closing span tag for deletion + $current->markForDeletion = true; + // Delete open span tag + $token = false; + } + } + + public function handleEnd(&$token) { + if ($token->markForDeletion) { + $token = false; + } + } +} + +// vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php similarity index 93% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php index 42d8fd40..fc01eebc 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php @@ -20,6 +20,9 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector protected $allowedParam = array( 'wmode' => true, 'movie' => true, + 'flashvars' => true, + 'src' => true, + 'allowFullScreen' => true, // if omitted, assume to be 'false' ); public function prepare($config, $context) { @@ -47,7 +50,8 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector // We need this fix because YouTube doesn't supply a data // attribute, which we need if a type is specified. This is // *very* Flash specific. - if (!isset($this->objectStack[$i]->attr['data']) && $token->attr['name'] == 'movie') { + if (!isset($this->objectStack[$i]->attr['data']) && + ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')) { $this->objectStack[$i]->attr['data'] = $token->attr['value']; } // Check if the parameter is the correct value but has not diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php similarity index 98% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php index 5377e5a3..2c96e307 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Language/messages/en.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Language/messages/en.php @@ -23,6 +23,7 @@ $messages = array( 'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', 'Lexer: Missing attribute key' => 'Attribute declaration has no key', 'Lexer: Missing end quote' => 'Attribute declaration has no end quote', +'Lexer: Extracted body' => 'Removed document metadata tags', 'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', 'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/LanguageFactory.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/LanguageFactory.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/LanguageFactory.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/LanguageFactory.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Length.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Length.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Length.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Length.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php similarity index 90% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php index 9f20a412..6d6f4864 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer.php @@ -230,6 +230,17 @@ class HTMLPurifier_Lexer ); } + /** + * Special Internet Explorer conditional comments should be removed. + */ + protected static function removeIEConditional($string) { + return preg_replace( + '##si', // probably should generalize for all strings + '', + $string + ); + } + /** * Callback function for escapeCDATA() that does the work. * @@ -252,20 +263,32 @@ class HTMLPurifier_Lexer public function normalize($html, $config, $context) { // normalize newlines to \n - $html = str_replace("\r\n", "\n", $html); - $html = str_replace("\r", "\n", $html); + if ($config->get('Core.NormalizeNewlines')) { + $html = str_replace("\r\n", "\n", $html); + $html = str_replace("\r", "\n", $html); + } if ($config->get('HTML.Trusted')) { // escape convoluted CDATA $html = $this->escapeCommentedCDATA($html); } + $html = $this->removeIEConditional($html); + // escape CDATA $html = $this->escapeCDATA($html); // extract body from document if applicable if ($config->get('Core.ConvertDocumentToFragment')) { - $html = $this->extractBody($html); + $e = false; + if ($config->get('Core.CollectErrors')) { + $e =& $context->get('ErrorCollector'); + } + $new_html = $this->extractBody($html); + if ($e && $new_html != $html) { + $e->send(E_WARNING, 'Lexer: Extracted body'); + } + $html = $new_html; } // expand entities that aren't the big five @@ -276,6 +299,11 @@ class HTMLPurifier_Lexer // represent non-SGML characters (horror, horror!) $html = HTMLPurifier_Encoder::cleanUTF8($html); + // if processing instructions are to removed, remove them now + if ($config->get('Core.RemoveProcessingInstructions')) { + $html = preg_replace('#<\?.+?\?>#s', '', $html); + } + return $html; } diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php similarity index 99% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php index eb421b23..b7d9abfa 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php @@ -384,7 +384,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer } } if ($value === false) $value = ''; - return array($key => $value); + return array($key => $this->parseData($value)); } // setup loop environment diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php similarity index 69% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php index 57173455..72c87635 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php @@ -26,13 +26,20 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer * Internal accumulator array for SAX parsers. */ protected $tokens = array(); + protected $last_token_was_empty; + + private $parent_handler; + private $stack = array(); public function tokenizeHTML($string, $config, $context) { $this->tokens = array(); + $this->last_token_was_empty = false; $string = $this->normalize($string, $config, $context); + $this->parent_handler = set_error_handler(array($this, 'muteStrictErrorHandler')); + $parser = new XML_HTMLSax3(); $parser->set_object($this); $parser->set_element_handler('openHandler','closeHandler'); @@ -44,6 +51,8 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer $parser->parse($string); + restore_error_handler(); + return $this->tokens; } @@ -58,9 +67,11 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer } if ($closed) { $this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs); + $this->last_token_was_empty = true; } else { $this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs); } + $this->stack[] = $name; return true; } @@ -71,10 +82,12 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer // HTMLSax3 seems to always send empty tags an extra close tag // check and ignore if you see it: // [TESTME] to make sure it doesn't overreach - if ($this->tokens[count($this->tokens)-1] instanceof HTMLPurifier_Token_Empty) { + if ($this->last_token_was_empty) { + $this->last_token_was_empty = false; return true; } $this->tokens[] = new HTMLPurifier_Token_End($name); + if (!empty($this->stack)) array_pop($this->stack); return true; } @@ -82,6 +95,7 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer * Data event handler, interface is defined by PEAR package. */ public function dataHandler(&$parser, $data) { + $this->last_token_was_empty = false; $this->tokens[] = new HTMLPurifier_Token_Text($data); return true; } @@ -91,7 +105,18 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer */ public function escapeHandler(&$parser, $data) { if (strpos($data, '--') === 0) { - $this->tokens[] = new HTMLPurifier_Token_Comment($data); + // remove trailing and leading double-dashes + $data = substr($data, 2); + if (strlen($data) >= 2 && substr($data, -2) == "--") { + $data = substr($data, 0, -2); + } + if (isset($this->stack[sizeof($this->stack) - 1]) && + $this->stack[sizeof($this->stack) - 1] == "style") { + $this->tokens[] = new HTMLPurifier_Token_Text($data); + } else { + $this->tokens[] = new HTMLPurifier_Token_Comment($data); + } + $this->last_token_was_empty = false; } // CDATA is handled elsewhere, but if it was handled here: //if (strpos($data, '[CDATA[') === 0) { @@ -101,6 +126,14 @@ class HTMLPurifier_Lexer_PEARSax3 extends HTMLPurifier_Lexer return true; } + /** + * An error handler that mutes strict errors + */ + public function muteStrictErrorHandler($errno, $errstr, $errfile=null, $errline=null, $errcontext=null) { + if ($errno == E_STRICT) return; + return call_user_func($this->parent_handler, $errno, $errstr, $errfile, $errline, $errcontext); + } + } // vim: et sw=4 sts=4 diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php similarity index 99% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php index 0d20c0ce..b42965ef 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php @@ -125,8 +125,6 @@ class HTML5 { const EOF = 5; public function __construct($data) { - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", null, $data); $this->data = $data; $this->char = -1; @@ -3903,4 +3901,4 @@ class HTML5TreeConstructer { return $this->dom; } } - +?> diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PercentEncoder.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PercentEncoder.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PercentEncoder.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PercentEncoder.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyList.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyList.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyList.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyList.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyListIterator.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/PropertyListIterator.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Composite.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Composite.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Composite.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Composite.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Core.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Core.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/Core.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/Core.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php similarity index 100% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php diff --git a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php similarity index 94% rename from 3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php rename to 3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php index c81b6b7b..03c92bd4 100644 --- a/3.1/modules/purifier/lib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php +++ b/3.1/modules/purifier/vendor/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php @@ -83,6 +83,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $this->injectors[] = $injector; } foreach ($custom_injectors as $injector) { + if (!$injector) continue; if (is_string($injector)) { $injector = "HTMLPurifier_Injector_$injector"; $injector = new $injector; @@ -164,6 +165,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $token = $tokens[$t]; //echo '
    '; printTokens($tokens, $t); printTokens($this->stack); + //flush(); // quick-check: if it's not a tag, no need to process if (empty($token->is_tag)) { @@ -219,6 +221,22 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy $autoclose = false; } + if ($autoclose && $definition->info[$token->name]->wrap) { + // Check if an element can be wrapped by another + // element to make it valid in a context (for + // example,
    \ No newline at end of file diff --git a/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php b/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php index 266998d9..7678e8f6 100644 --- a/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php +++ b/3.1/modules/moduleupdates/helpers/moduleupdates_installer.php @@ -24,7 +24,7 @@ class moduleupdates_installer { $version = module::get_version("moduleupdates"); if ($version < 1) { - module::set_version("moduleupdates", 6); + module::set_version("moduleupdates", 7); //Remove the ModuleUpdates cache entry 'JIC' Cache::instance()->delete("ModuleUpdates"); //create the blank ModuleUpdates cache entry with an expiration of 0 days @@ -34,7 +34,7 @@ class moduleupdates_installer { } static function upgrade($version) { - module::set_version("moduleupdates", 6); + module::set_version("moduleupdates", 7); //Remove the ModuleUpdates cache entry 'JIC' Cache::instance()->delete("ModuleUpdates"); //Empty the ModuleUpdates cache entry so our new version starts from scratch diff --git a/3.1/modules/moduleupdates/module.info b/3.1/modules/moduleupdates/module.info index f09bfb2a..bbf3516a 100755 --- a/3.1/modules/moduleupdates/module.info +++ b/3.1/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 = 6 \ No newline at end of file +version = 7 \ No newline at end of file diff --git a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php index a5dc4a0b..cc1e92a4 100644 --- a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -26,34 +26,47 @@
    - - - - - - - - - - - - "> - - - - - - - - - - - - - - - - -
    Installed") ?>
    "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?>
    Installed") ?>
    +
    + Core Modules + + + + + + + + $module_name): ?> + + "> + + + + + + + +
    Installed") ?>
    "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?>
    +
    +
    + Community Contributed Modules + + + + + + + + + $module_name): ?> + "> + + + + + + + +
    Installed") ?>
    "; ?> *"; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?>
    +
    \ No newline at end of file From 13e0d4aea463645b2fff4675c6bd0b4c9686f3e4 Mon Sep 17 00:00:00 2001 From: Bharat Mediratta Date: Sat, 15 Jan 2011 13:31:02 -0800 Subject: [PATCH 239/300] Update all head() and admin_head() theme calls to return the results of the $theme->css() and $theme->script() calls so that if a theme does not do script/css combining they'll still print out the appropriate ", url::site("highroller/pick_theme")); + return $theme->script("highroller.js") + . sprintf("", url::site("highroller/pick_theme")); } static function header_top($theme) { diff --git a/3.0/modules/kbd_nav/helpers/kbd_nav_theme.php b/3.0/modules/kbd_nav/helpers/kbd_nav_theme.php index 6540f020..e085f187 100644 --- a/3.0/modules/kbd_nav/helpers/kbd_nav_theme.php +++ b/3.0/modules/kbd_nav/helpers/kbd_nav_theme.php @@ -3,6 +3,6 @@ class Kbd_Nav_theme_Core { static function head($theme) { - $theme->script("kbd_nav.js"); + return $theme->script("kbd_nav.js"); } } \ No newline at end of file diff --git a/3.0/modules/language_flags/helpers/language_flags_theme.php b/3.0/modules/language_flags/helpers/language_flags_theme.php index 42c16191..9b5a7ada 100644 --- a/3.0/modules/language_flags/helpers/language_flags_theme.php +++ b/3.0/modules/language_flags/helpers/language_flags_theme.php @@ -19,6 +19,6 @@ */ class language_flags_theme_Core { static function head($theme) { - $theme->css("language_flags_sidebar.css"); + return $theme->css("language_flags_sidebar.css"); } } diff --git a/3.0/modules/navcarousel/helpers/navcarousel_theme.php b/3.0/modules/navcarousel/helpers/navcarousel_theme.php index 844af7f2..a22d42fd 100644 --- a/3.0/modules/navcarousel/helpers/navcarousel_theme.php +++ b/3.0/modules/navcarousel/helpers/navcarousel_theme.php @@ -36,8 +36,6 @@ class navcarousel_theme_Core { }\n"; } $thumbsize = module::get_var("navcarousel", "thumbsize", "50"); - $theme->script("jquery.jcarousel.min.js"); - $theme->css("skin.css"); $showelements = module::get_var("navcarousel", "showelements", "7"); $childcount = $theme->item->parent()->viewable()->children_count(); $itemoffset = intval(floor($showelements / 2)); @@ -64,7 +62,10 @@ class navcarousel_theme_Core { $onwinload = "});\n $(window).load(function () {\n"; } - Return "\n + return + $theme->script("jquery.jcarousel.min.js") + . $theme->css("skin.css") + . "\n - - -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    diff --git a/3.0/themes/greydragon/changelog.txt b/3.0/themes/greydragon/changelog.txt deleted file mode 100644 index 3d5a1451..00000000 --- a/3.0/themes/greydragon/changelog.txt +++ /dev/null @@ -1,239 +0,0 @@ -=== 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/3.0/themes/greydragon/controllers/greydragon.php b/3.0/themes/greydragon/controllers/greydragon.php deleted file mode 100644 index d6e51c8c..00000000 --- a/3.0/themes/greydragon/controllers/greydragon.php +++ /dev/null @@ -1,39 +0,0 @@ -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/3.0/themes/greydragon/css/colorpacks/carbon/colors.css b/3.0/themes/greydragon/css/colorpacks/carbon/colors.css deleted file mode 100644 index 57fd30dd..00000000 --- a/3.0/themes/greydragon/css/colorpacks/carbon/colors.css +++ /dev/null @@ -1,192 +0,0 @@ -/** - * 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/3.0/themes/greydragon/css/colorpacks/carbon/images/ajax-loading.gif b/3.0/themes/greydragon/css/colorpacks/carbon/images/ajax-loading.gif deleted file mode 100644 index 0996045a0978d28e0ac2fb83a634bc349cfab407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4782 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 diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-album.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-album.png deleted file mode 100644 index ac87ec4fbf6acb75ac29259e60d8a9a0992cbef0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA3y|d6q_!SNv7|ftIx;Y9?C1WI$O`0h7I;J! zGca%qfiUBxyLEqng6t)pzOL*yIe{8wlsqH7fI^Zbt`Q}{`DrEPiAAXl0g0J;C3=3Y zAqr*2dZv0NcD-lHfQn9fx;TbNTrR!1-|KLKgxkmXN8((Zi^|H1i>6eu&)uW=Q7Xo| zWA@_khq6m{?A&S5q0ljHmVJDXY*3-+{iizZ%>4OGF?VN4?l$6ilYV{ip{4eW-1%{9 zO{?~1yo%H3`FQqaZ~0E+&MR(NFD2)0(BAi9lW5tI?4t@LFH{yC^V06BatS`-c;k4x z{FSJ!JUb`XKRnT>-|4e-lIDjWwbO$+1*^G^tA%OxMsf5QL( diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-error.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-error.png deleted file mode 100644 index c37bd062e60c3b38fc82e4d1f236a8ac2fae9d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&N#0$9Ug7g~-`rQ^qx~m@y2OU8A z#zh~=7n#Z$Z*fx-GOtDf07cgx0suCz_W(2~Y(0tf@FX@P6EPuM_dgn$vj9LucO)%W zw%HgMW>=#oL>nZ>M&NEf08>)#)k<{$fCT_r>rPi=BV=hFh6WS^qqze>C6Ek}o{M5% za|@JGowu0t{&hgNzySHZxy@LTNh);YzZ2zSp_ zl$^T&Dnc|NLb&RD_!4>pt@VHdP)ZGER%5ZmWEe$lryR&y;2u^3cOkO4#6c%-(EY6a{600000NkvXXu0mjfxS2AI diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-separator.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-separator.png deleted file mode 100644 index 3e158515556616fcf3cab5e837664263f6c58c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VkYHF5IUx~9v7|ftIx;Y9?C1WI$O_~$l?3?( zGcc4*K5GHwNtC!olmzFem6RtIr7{F0X6BXX`MHKDlo{(8o2`8QNEN6?(bL5-gyVX0 z!U4v#yhv;IWAnD9iq5gkd_O1j#iA2gADI~V9C;r3-B_CiRLtP%>gTe~DWM4fa9k}Q diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-success.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/ico-success.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1p004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000id000id0mpBsWB>pF8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(0m?~4K~y+Tz12-i0znuD@IBO5QheEUch$Ul5JYNkUeu%nsY5STrsXZH%sPk~ zimiH4@S+F(3|0u`C-5MiJ^XVXFtD^Xlm>=*XN{J1p>GdwyygQ}{i zR;$S8^T_3L6xV7sTqF_;(YfGmcn_sg3B_U&UauE+yB(4w!Q=5DnM|V5XyEYpcrH2@ zyt=+YrBWeGuh&!8>2xp{3~)M~G?7{^YX>u#46@lQ91aJ}W;1x6hsk83*l08&olc|I z>tQDp`i9QUuuv$_k{QUB%0NL7=*qI}bUI)y8jEQITdfvGqY+gN14U7UEXx$TTrLcU zLjryNfHv?+Ez^K*w+ls42xOSs?WVnD?d|4hCtkFAzY#->1nKdNg^z zl3JdkH>uQWK%d`_%P(d4>yrO^L=tG?Eh{@2ZDlPT6>VJ=9X(ZDeN{aJH9bQ$eIs=PV@r3Rkmy~xKrNCbt`Q}{ z`DrEPiAAXl0g0J;C3=3YAqr*2dZv1Ye$Op_0u{-7x;Tb#Tu-(*@TIYnk;A?rGeoFk jrsWKY8I7Mj5||kR?70MU=8OLTDrN9=^>bP0l+XkKClfE| diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png deleted file mode 100644 index 7d1723bf552fcc1f15a617b2246558416c012cba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9312 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 M%34a5iq_%(2bpt6pa1{> diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png deleted file mode 100644 index 5442fa51321e3a2a4f0d2ce50fa651316b729fc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDVB6cUq=Rpjs4tz5?O(Krjj7P zUE~zhw+=#aM-TBZ>d2^i z96H{8s@uD9!I~`)N;AFTzp-I`lJtPuXWe0 zc6zRUUguqgqtHInx3~UCbLZz-Y)&j`m zEbxddW?0j z`Mwlz`*|=mo;l+q@?OK4ah8EhL`srqTcw;k$0X4WY6}`n9YxI?S=MkaY%pCIeqdW7 s_l<35%tYR^I6Fp5e`{dvo(%LzPTB5*?IE4FfqrE0boFyt=akR{0KG?&;s5{u diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/view-full.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/view-full.png deleted file mode 100644 index 7145fd9d49d9231cfac264af85e5664f82fbcf67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-s5C}7hYIrpO1tm*dBT9nv(@M${i&7Z^5;OBk^!!{y6v~YCO!Z9cde4*r z6*YOfIEHAPPu59vV1J>cs^f5IdUNK|_x<_qjx!n?IW-QQIC0{@p%W~}1O(LG_M_NP&)JlnCE#pg!>q>BGHyKkjSU6FrT)tJy!>mw z&$QWK-Gu}dB@?yH#_Ee_A9h)!@9@m$Ns^DFX}i=yMur{toz;c;MYjW8!{F)a=d#Wz Gp$Py1x@KVj diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png deleted file mode 100644 index ebd042373ea4517a820093cd18f823a9a20a8c03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDVB6cUq=Rpjs4tz5?O(Kg=CK) zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a1C(GY3GxeOU?`h>)&j`m zEbxddW?8eR=RLCF%=h?3y^w370~qEv=}#LT=BJwMkFg)(D3Q#}*A-ZN!D zMKe5I978nDCuG$;zFqT6?njH=WvBz2Fdc^lpJgvANs}4mnJ) zNEehi004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000ib000ib0l1NC?EnA(8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(0c%i9R7C&)02mk;PEJlFBqSOd8XzDbR8&+_Qc@-+CQ(sQWo2bpSXg9aWEmM5 zDk>^0EG#fEFf}zbH#avqIXOByIz2r-KR-V}KtMr3K|@1BL_|bKM@L9VNJ>gdOiWBq zPft`-R9aeEUS3{bUteToWMyS#XJ=<YbpP-Cc> zsi~{0tE{Z7t*x!DuCA}IuduMNv9YnTva+sxVgExy1Kf%ySu!+ zyuH1>zP`S|z`(@B#K*_S$jHda$;r;n&d<-!)z#J3*4Ee8*V)lq(=H}<;=Lxr}wEzGB4RlgYQvg0dLq|-Hp{uXa(%zSwdA|Sv0UAj}K~yNuozU4M z0$~&d@UbtsvSztR+0#%Vv}c4yK1-ABYZ1nlp=jvO_vp^ZOXupZtU0}MZ8UGjAF%800000NkvXXu0mjf@Wq7L diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/view-left.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/view-left.png deleted file mode 100644 index c59af5d00dc95a3a9e5d09523e1795d93b9ac4c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-s5C}7hYIrpO1tm*dBT9nv(@M${i&7Z^5;OBk^!!{y6v~YCO!Z9cde4*r z6?J;LIEHAPUwdIAZ?l6)^TYf8;T!Ipo!HmKE74$iX(NmF+;xXUJO$;HyacD~*`2)R z;+MCK>5AgKt=rdr+vdnHNrS7aDO^~HjmLp;NdoJ&;Nr5L$;oqM9zS6?$vElo?GI=5 zVz;sH-@B3LV?@>L>Gz)PHZQ4ER&h)C6QQ_CI6hn?A>F^Pu;|~%V~k5R+(epMs%3zV OV(@hJb6Mw<&;$VYA8Shh diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/view-right.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/view-right.png deleted file mode 100644 index 595054562d3a26ded057dca1ce7d79325d48ee14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-s5C}7hYIrpO1tm*dBT9nv(@M${i&7Z^5;OBk^!!{y6v~YCO!Z9cde4*r z6?J&JIEHAPUprwVZ-at>>-%_(m~)nI*4#P3IOp&=)tHk3ohKUSthnfOVtGZQ_`Ai6 zi>59y{ig;b?>_XKnTaoV6FL20)dHZK O7(8A5T-G@yGywoR&v1nR diff --git a/3.0/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png b/3.0/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png deleted file mode 100644 index 2fb53ad09633e73cf42669bb918e1e42aae85b2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1014 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000ic000ic0Tn1pfB*mh8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(14l_jK~zXfwUzBplV=o%`2+TGe+VRen~(qjg4+i>5()+BMqnlkEy2?8VhKT_ z&=9bUECYjSmQjuJB8qP9W_c?jP@)vv2FTbZPfJJ3HZv*M!DXMvv;kXa5qXl6`^$Z< z`<(m!o!=9qR;xLVh!LSN@A;FM(SkEho}|6Koq~dbh=oK1r>Cc5O@gq&fUAg1arTII<=0H91YqpE|Hs2$WK4m=<5C+<(YHz_T3BaH5d$g z;9)Bcz)k&wlzyhAs;CK#vXM_tD7akPhJE%AK0cL8YKDTY>wUOfE)o+HBZEcd1==jm zS5sZ0qr9*lO>r<>5Byd5~r1Bg-m6SoQFBwPOChk5^rNL2C2rSDjeR;|PnZc>eO= zsUPZEbyHfV!FcmF^NYWcl#~?tjstU&oSYn@2;up2X6<&y#~(2~GJ@a~P?f5pSA__V zjg8HjoSa~8ZkAi!H>s+s;6%bl(s0;pk6Bw=!(uV-tK|a+#Qz{4hMV-bxVR6r+8T1Q zK}^oz2jwmG!^>34#K^-RtJ&2CaI1^aJvp@x~sY@5K&3Kl+tkLqA&^Fg$L< z>pvlI$o%{~GMOy02`Lo0Qqr__hOZm0QF~U0zN!P&=?WfCEn^y*;(T=-Bcl^R_`twG zWN>myisTbEtDSnap5{sey7QfUE>m%}?FNG5Ka_H~rR9p$X5aOGewkiibKOt- z-+!@7Ax-cf62div { 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/3.0/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif b/3.0/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif deleted file mode 100644 index 0996045a0978d28e0ac2fb83a634bc349cfab407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4782 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 diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/background.gif b/3.0/themes/greydragon/css/colorpacks/greydragon/images/background.gif deleted file mode 100644 index b8083564eefae82ff216a242edd47c6ae4914396..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1098 zcmZ?wbhEHbWMn8|`py6WjEsy-j7&hl%*@Qf%mPHLEI`D{#>xgn>}>2DY#bcy9GvW& zTpU2e1w`DO+&r8-JX}1y+`N3;eEd8>#4o_hFUTt($SWwsCn(G(B+M@?!Y?c;07N39 zf}&!AqT+&L;zD8)!s3#`5|SbkQX-PlqEbL4BPJ~?CL=2@BPT8^F9AeyK%^ikuPCLU zB&DbY^Zrm3K=rJ$jusHv@}siUN&tEOkDZD<1Z%_ub*0z`*^ z4oD>^FEDWYXJF)z@z}87U^53V8;3!I10yqsh)%+T2aPS9qB;pDHY_~ME~xA^!((BS zYqz*j#+40)%dj3 z-Bca6uDZH9j61{hz}D5**RdZ`lJMU4<_1f~Wvz(QWpA6d7d+mV>M%X8d3S~Ei@UqO zH^%Q5P~w>8u%qB1XNR!=f&()OTaQnWkDlRE`Kk5v4E^LAdkmY?&r6x)NcdJUw_jcn zSpMR{!mY3QHzXfs6I`^dI>ya7v&(&6UzkhIeyMBS=y`$ZqUtC_^eo)S>=lh4p$0sNzxBdP7VM%xNb!1@J z*w6hZkrl{SNcITwWnidMV_;}#VPN>O+y9PAvN?3`R2K*R+^+??DzoIE^SJiOez zeB6BeJp4c;z$+lgD=5S#D9kS;%r7h=AS@~%A}S~7WUg8>25}cn_Ql40p$`Fv4 znOCCc=Nh6=W~^tr*LCqppb`^L7sn8e>&XTQzs?_NVEX^~$=~+NHzHOjaeT7x(pYt( q-bD6>!7dQBLhryUvosZXU|3CAtoLO}sOv+Zeg;ohKbLh*2~7YHl27LV diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png deleted file mode 100644 index ac87ec4fbf6acb75ac29259e60d8a9a0992cbef0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA3y|d6q_!SNv7|ftIx;Y9?C1WI$O`0h7I;J! zGca%qfiUBxyLEqng6t)pzOL*yIe{8wlsqH7fI^Zbt`Q}{`DrEPiAAXl0g0J;C3=3Y zAqr*2dZv0NcD-lHfQn9fx;TbNTrR!1-|KLKgxkmXN8((Zi^|H1i>6eu&)uW=Q7Xo| zWA@_khq6m{?A&S5q0ljHmVJDXY*3-+{iizZ%>4OGF?VN4?l$6ilYV{ip{4eW-1%{9 zO{?~1yo%H3`FQqaZ~0E+&MR(NFD2)0(BAi9lW5tI?4t@LFH{yC^V06BatS`-c;k4x z{FSJ!JUb`XKRnT>-|4e-lIDjWwbO$+1*^G^tA%OxMsf5QL( diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png deleted file mode 100644 index c37bd062e60c3b38fc82e4d1f236a8ac2fae9d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&N#0$9Ug7g~-`rQ^qx~m@y2OU8A z#zh~=7n#Z$Z*fx-GOtDf07cgx0suCz_W(2~Y(0tf@FX@P6EPuM_dgn$vj9LucO)%W zw%HgMW>=#oL>nZ>M&NEf08>)#)k<{$fCT_r>rPi=BV=hFh6WS^qqze>C6Ek}o{M5% za|@JGowu0t{&hgNzySHZxy@LTNh);YzZ2zSp_ zl$^T&Dnc|NLb&RD_!4>pt@VHdP)ZGER%5ZmWEe$lryR&y;2u^3cOkO4#6c%-(EY6a{600000NkvXXu0mjfxS2AI diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png deleted file mode 100644 index 3e158515556616fcf3cab5e837664263f6c58c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VkYHF5IUx~9v7|ftIx;Y9?C1WI$O_~$l?3?( zGcc4*K5GHwNtC!olmzFem6RtIr7{F0X6BXX`MHKDlo{(8o2`8QNEN6?(bL5-gyVX0 z!U4v#yhv;IWAnD9iq5gkd_O1j#iA2gADI~V9C;r3-B_CiRLtP%>gTe~DWM4fa9k}Q diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1pPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7j#8fbW?9;ba!ELWdKlNX>N2bPDNB8b~7$Dy+xzR0009dNklz5iy(q%bSqVS0aQS#nyS!B)p~2JwYCSNwi-pf zXyQc=`WdXor1}Xw7!Pfl`10$_n5EkEpmCBXGsDjB+4;}z@<_7UiU0%)i;BNJe)5zQ z6cm#Cw5zL2^843Ma(Qu~(%dpJOO^(Abq!kLJ%|rvkW3HXi}+v$Er}j@0=1xYZe7wHCj$O}c)(ICEdVHvFX+uM6JLmm}Nvy7}a#>kf8K<9x!909cvO|5NMSXkgCGa)OL2~8#w@7XSQcX#P`0Axo- zXoltf!i9?43#ZQyS9JjOjUjArZ{zIj45?I#3q?_cEX#cC_xo{rddh{7XpCpBxz1P6 zYKh|Vz~*pq93C&0b1OWsx3>pXRk@JKg25m^Th@LY9l=T0YB{Y}D!m|fXC-92lBX(A zgT3$jJjmM4&JMP=ws3NClK+hj-j0sm$}tD4mJ3Cz4Q7iqk4>q-#>U1i0h{a}9OT94 z=jRw19fO66w5;OxP@%&OqbR{>7H=WYGTEz>$z+htX3^2v3A@9INGyiq<70j^CLN;J zwXB^UA1S9oJ$r)mwYV#hXf!XKoSNb}y-@(GmRJ=hpOoqJcZRKJ(s%ST^albwr!yE~ zt#E<}v|K@wQ9{3nhrr42ZDlPT6>VJ=9X(ZDeN{aJH9bQ$eIs=PV@r3Rkmy~xKrNCbt`Q}{ z`DrEPiAAXl0g0J;C3=3YAqr*2dZv1Ye$Op_0u{-7x;Tb#Tu-(*@TIYnk;A?rGeoFk jrsWKY8I7Mj5||kR?70MU=8OLTDrN9=^>bP0l+XkKClfE| diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png deleted file mode 100644 index 7ab15cae7d3a3fe41b721abe9ec09345d1e5559c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9657 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 UeJ;!CAJ3<%sHspTZxQ~#056d*x&QzG diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png deleted file mode 100644 index 206ccd66e043f44319ed6bf432b3c20cedb36179..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 449 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoCO|{#S9F5he4R}c>anMprB-lYeY$Kep*R+Vo@qXKw@TIiJqTph(ejM zo~fSkf|rq3fu}8f=?+J9cf(xHgB?!Jz)%B3Xrf zye?n0#L_qt9%?uS)$UobXNL&KDX;GEh9BE_t|??~zwEbvZRhHuXIH+JH2=896Q=OW z>;2Kp>CfiGRn+`kI9FkxXtQd^t(3h-OG97$c{1~;tgga7)n?0%U8Zku{gLL*&$HO} z?n#`$eHo=acPrYj&U#?kY0R}tYGTWKpZ)v(JbLwccK`d^wS1lx%}=G;r#5y!P8a)f zRe{sq`tP%AuV?44-&6hP38#yM`-~g6XD2fxa2z}GWXgi(84_(0!afPIhXa@vO=K2R ziD43ShD0or;$lD}j nnV@*+#-SrT5+Mg?MmPyDm^-{Uz^N|@3SkCMS3j3^P6 diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-comments.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-comments.png deleted file mode 100644 index 293c587e31161b509a6a2deb8891e03752e78a5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 492 zcmVPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7j#8fbW?9;ba!ELWdKlNX>N2bPDNB8b~7$Dy+xzR0003;NklZCx3_mh19tZI4EB6jfIWi^m}VD}wekb)7-ma<{X%6$j4{RV8zF0w_?S@ z&ZX7GwX<8A7Oq&~&BtfOV9CelG;La2XtjewXmdhr_q1s~e0-J+=0JOk+Zq$X+R~G2 zLL5rl%7BhA2MP0eCKX19lqSUH1-oUYdh)@9`8?c$6Ouq6Gc%J9ANn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-sFbFdq&tH)O6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q7ALkGu1P(>pfEj zRCLzU#W6(Vd~5$%t|kYO*7K`besQQpNwIESt@9#1>(&kJ9lg`GYU^rjeG$6b_~452 z%Z-~Y0$xhMyXFI3MKMQ;^UtKr;%wmpe{)w~P hjrv=ZrR%4k6R(xZJbzz1|2NQM44$rjF6*2UngI3_j9vf$ diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png deleted file mode 100644 index ed76257a8cfc001029190d078470850d16cc7d2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDVB6cUq=Rpjs4tz5?O(Kg=CK) zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a1C(GY3GxeOU?`h>)&j`m zEbxddW?{VmR zbkpwLuU|iVnr`&j(M7pgQILa=qgJ8s`7NDkhEg||^t+l@YK2%u=We;XCODzNfiH2A z_oRS6#;RP6@GZgo`@DaDh`AM9wJF%@%C9~5UwL0T9QpG5>gw;WS5B9x_;027NoN+D z7KHgp97eaYbI>gTe~DWM4fA=8($ diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-info.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-info.png deleted file mode 100644 index 521439ce7c35951c231bf4759dd2874fc918cffd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 938 zcmV;b16BNqP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000ic000ic0Tn1pfB*mh8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(0ew(RR7C&)02mk;PEJlFBqSOd8XzDbR8&+_Qc@-+CQ(sQWo2bpSXg9aWEmM5 zDk>^0EG#fEFf}zbH#avqIXOByIy*Z%JUl!-Jv}}?K0iM{KtMo2K|w=9LqtSGM@L6U zNJvUbN=!^lPft%%Q&Ut_R9aeETwGjUUS3~cUu0xtWo2b&XJ=?=XlZF_Yinz4Y;0|9 zZEkLEZ*OmLadC2Ta&vQYbaZreb#-=jc6WDoczAevdU}0*eSUs^e}8{~fPjL6f`o*G zhK7cRhlhxWh>3}bjEszpjg5|uj*pLzkdTm)l9H2?la`j2nVFfInwp!No1LAVpP!$g zprE0lp`)Xtq@<*!rKP8*r>Uu_tE;Q5tgNlAt*)-FudlDLu&}YQv9hwVw6wIfwY9dk zwzs#pxVX5vxw*Q!y1To(yu7@)z;S5 z*VotC+1c9K+TGpV-{0Th;Na!u<>uz*=jZ2o4wQ)i000eiQchC=fEKKD8IfKqBJP%EXNC?X20K9$$o+U(W3u^QVAPXFxOl9nWi0)X&? z&1*ZdOo+0(D|K+V%&Q#9gvg2#YUVKPu?!)EoF{+s8W>&U2WRI>pcnZVO?_SuGmP+G zCzJ|*L@7kkg41$bDS*Y@Q9MlGRE$2t}mS&_C7qS&y2mPs_N$O(B0GPD`VQri#o>Ps+5+?F_zP` z(?LGTO!k8@#=aih`MIa0_s=g)Stxi1bRte8^Iq+`2OY}00000 M07*qoM6N<$f}e<{V*mgE diff --git a/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-left.png b/3.0/themes/greydragon/css/colorpacks/greydragon/images/view-left.png deleted file mode 100644 index b5b93c7a5e0e191565a8970a8bbae7cbcbddcca5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-sFbFdq&tH)O6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q7ALkGu1P(>pfEj zRCLDE#W6(VeCq_myh9EGuJ?PE7jTEJ^6;2u!5FfyDc`}>`MLKG20M2vy@QG?E-h<1 zx8tK9*X-2ItUsSlewO)fdTr!{l&XNw=37NCO!;-S=kQX=cJ5X`1*I7pYFv)8AuQ}N z3NxBJirCIE{8PF3Sn9yXD~Ej+vF|>sQkA#OM$Gnw(<1dr)7!OPhn`$ELF7lu)#qBO z-}C!!UcGqj!t|6Q44xmT3$r1`;yDe%%u3)VqWFH*vAvT iTAj%IJ+W#2YyEKX@E@h|rItW{F?hQAxvXNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-sFbFdq&tH)O6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q7ALkGu1P(>pfEj zRCLzU#W6(VeD4H9zC#8)uKVxu#J)Vz(h|}seBg?9L9<`%jU6oP4`gy;nw$+b8ckhW zlIirwL}jf+!KbA!PX6oj-EHG6bXZz+S)k_Zjqi8Q)_BfsB*);Yqom}pnemP2MuF7% z;T03lHW#Y=s5oTxOCo%=004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000id000id0mpBsWB>pF8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(1C>ccK~zXfwU+%)QehOw`2+f~KSVb-r=m`sI;$TdH&HYxr!|ej88Opf&Ri*T z8s=K+Y^)`<@+DI%sL+%$3umIesF{^skOX^Kic0#8=V_P8%T=6px3lN&-t&CUIrsUV z1H9PSSg;ls7l++gOiYZ2#UoJOKfWk4Sa8a|eQ0QCKu%81wuw+CpU>xXOioTtI3t(A z(E9{dixqZ9v<%XkcI0ydr^2qfs(vx$i-DSz!%|S zSv|~SpRxa7CK6LbXt~(|o6QCyv(3VyOkXf@V9PAXmZ4Ol!kOF($P3EQP zYzWfwz|1?~n=RST2`()?N6T!w+zg|B5X{s(9ADkv)D5-G+fi5~hxYbeOiX>G4dvSg zoZ$HQct1rjv$Gg8n=v@}0#Ez7!3hot3CVc*atPz&W4P0L8zm*h;73Q% z<}jH?u&}TIgF(-!Wlk_D&3zzy19f_6Xy_h=LWzuY;nv__TY4ws#4{_%4N3oB7&|F> zSy@>iCXq-WlgV~%ERTYBQf4r|lu5D-#yMLoMq67OM^*XpmDrbOzU}1VX`UZ?x{Px`gVu732m6c`Gsnr4D0l_!4x4eL^pBs4l;yv0m zomgE4o(`JebVp!=OiWBbAP{&32nZI5GU=va<0!6FUPIYQ71Sk-kRC3^$g3IX`d;C5 zX*v1_hCJ}@?(Xfu@d*iZPMC~lRLIn*Ije!{bTf_$q-d(Y1;+9V;#?_2*;42p4Z!7c zAvHC1doWQ5`BnJoScbBw618R5P*r>tDrG&|wD+;$0`iU*A+O*poNIt@wq!r2Dr##l z(K0)3cH-UeBvuyPwBPRU>Rw!H8>qOTg5F?+eenm<($f46kP|Ev3jGw}Tv~#KVK6y4 zh4D9U;9Pa$e8u_TRY6KgOiaY|^aoh27WDQRP*YP4!J&h+Ic8?QV10cZqocz+(=h>u zg@w@rq5ltjuB4lB0l0000< KMNUMnLSTaZwCpbc diff --git a/3.0/themes/greydragon/css/colorpacks/wind/colors.css b/3.0/themes/greydragon/css/colorpacks/wind/colors.css deleted file mode 100644 index 09ab5a6a..00000000 --- a/3.0/themes/greydragon/css/colorpacks/wind/colors.css +++ /dev/null @@ -1,184 +0,0 @@ -/** - * 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/3.0/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif b/3.0/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif deleted file mode 100644 index 53dd589fa194f5db985e4301c7a73ed4f1b9ad99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2545 zcma*pX;2hr8VB%~zPqQp=@B)`y1PS96NXtx1_oS2g;AJ+5se@Q;|&UOs2qwM!p4Ca zhJiUal?ExBdstaT~?f3m?cZgNh{frmzMrfcJ8)3 z;P)BJevgQNtw(cfF&90SnBnnr_M&xm@O%6 zzG;!w8(-4o!w_SKEsx_t8i2dHA{$xug+5j_t}~5!rEMsQGpY^uf8#e!ez!6*c<$`# z%2he_t-h<5RT`BLN|GpKWC}6HY^k+5Dom~L=dCyXf!71bXd=Do;)LbM zv+gGZ*&l+=TYuPh^HJ+{Um3v9W8Dqi zZsIDi!j*rKg;1w`oCOc;={#&)!ZF-E-<0u^i2Q3W!xltD%v-T4#;x)CfF(1Ouv)`p z>uUosDu9aCbKX*^H)pSF(Clw+wK}`3LC8Vd5wn~@j##n&8u(Or9|$_$-*q^|8tR^Ze!v`8XRU12fiFxN&Z^HeB_6{>ZFruBWn5U6U_WQI--B zG~o}Ys+mlEb+PC3o7FVVvN+9%m7%O}Y_c3WoYls|wT3|PzzV{wM+0F|$H`%9CT0Y^ za*mZIUl3}$c$+|-?~lU1Lc0B}sn(uvcimZ5#)MR#za@NWsrn^X=yb^$k3_>|w9gkR z>#sW<_Ea0KR$g0dg}fy7K69z6?%wR47d!o@ zGKEctobW(W=VY|Az1{V$NH$hYU5!_r3z~t<1_3#$IhT`+-0m8Lu3WANjbGvNg1vn1 z=E&h$lM^*FtCrg64J+q9{~$Yc&oK@)FJ9|wdu`CBEMKgD->DcQRuxrUW36azX6kqE z(WAIz&9a^4uqfQXZ?4*+k}M^TI=80q9GA}G8+>bb(fyeR`Q-S93>Q-=OX7E#U^f4~jWk3u>uE&r}55)J9D8+*U}{&~xy z+xheT3lqZ$_0RMat&nbp<_ki!f?+E=jik2kBu@#UnX z0AcCVU-1x`*#iFAGPG4?H)8U+YtUJCS?At3FP6 zWjMo76`C~5oga!z%d+xx$x-ljIYP1$9YjNGd0H&jsWL7XsM^)8O;wB>PVnYYXxtSh|$ifidD)RGQ}R zwHJrNIdVjTqdEklY&;>>dZRxDK`F>#B8Ki@pp8f7rwM2mppszvcw{I`Q1%>g09l6Ekr2-S;;<|g2awum zsj!Q?)Pbyi5x+M!F@WFk6jsdhHiry`x0GIzjy;Yj%rtk*Y|Rg9EEqf3wBpTM<@>(0 zk0}s;bd@I0{9$O~|I{qUIC4vLX&EGrCS`%OyAe_n3}9mSiO7}(QE&#Jdx176W`mkS qJBjK|(6Aas*VCO0)PD%QJkkj;=v4CXc=>?~L(zU6eu&)uW=Q7Xo| zWA@_khq6m{?A&S5q0ljHmVJDXY*3-+{iizZ%>4OGF?VN4?l$6ilYV{ip{4eW-1%{9 zO{?~1yo%H3`FQqaZ~0E+&MR(NFD2)0(BAi9lW5tI?4t@LFH{yC^V06BatS`-c;k4x z{FSJ!JUb`XKRnT>-|4e-lIDjWwbO$+1*^G^tA%OxMsf5QL( diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/ico-error.png b/3.0/themes/greydragon/css/colorpacks/wind/images/ico-error.png deleted file mode 100644 index c37bd062e60c3b38fc82e4d1f236a8ac2fae9d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&N#0$9Ug7g~-`rQ^qx~m@y2OU8A z#zh~=7n#Z$Z*fx-GOtDf07cgx0suCz_W(2~Y(0tf@FX@P6EPuM_dgn$vj9LucO)%W zw%HgMW>=#oL>nZ>M&NEf08>)#)k<{$fCT_r>rPi=BV=hFh6WS^qqze>C6Ek}o{M5% za|@JGowu0t{&hgNzySHZxy@LTNh);YzZ2zSp_ zl$^T&Dnc|NLb&RD_!4>pt@VHdP)ZGER%5ZmWEe$lryR&y;2u^3cOkO4#6c%-(EY6a{600000NkvXXu0mjfxS2AI diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/ico-separator.png b/3.0/themes/greydragon/css/colorpacks/wind/images/ico-separator.png deleted file mode 100644 index 3e158515556616fcf3cab5e837664263f6c58c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VkYHF5IUx~9v7|ftIx;Y9?C1WI$O_~$l?3?( zGcc4*K5GHwNtC!olmzFem6RtIr7{F0X6BXX`MHKDlo{(8o2`8QNEN6?(bL5-gyVX0 z!U4v#yhv;IWAnD9iq5gkd_O1j#iA2gADI~V9C;r3-B_CiRLtP%>gTe~DWM4fa9k}Q diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/ico-success.png b/3.0/themes/greydragon/css/colorpacks/wind/images/ico-success.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 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 Pk4-YvHMw4)?HKcaF3|lA diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/view-calendar.png b/3.0/themes/greydragon/css/colorpacks/wind/images/view-calendar.png deleted file mode 100644 index 13e0e8fa6ad01636e87a376aebada4612d937186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 637 zcmV-@0)qXCP)D7ei;IhljEs$qjgF3vmX?;6mzS8Bn3dCU$;rvf%gfHr&d<-!(b3V;($dq@)6~?|*4Eb7*VowC*xA|H+S=OO+}z^g z;^X7vlt)=I7_<>+9?6?CkCB?e6aG^Yioc^z_3;Z)X4i010qNS#tmY4#5Bb z4#5Gqk!$S$000?uMObuGZ)S9NVRB^vP+@6qbS_RsR3LUUE;TMRK8?(F0002gNklXRGqXq|E9aC%$SE>sC?yodM2ylL?*BGC|I-$i?Rt0TTOKCqib?KW zAaB90l*C7nq)MMQ@)~3h^ULGCrj5J;?dq0`!%dv4(?)hc4JqL+D@Vn|eU3Kr63o28 zPei?})dmV}A;b!Iw2?RPt^X|6*2dg^Iu32*oeSh65B?wfC&wND X`katw^)ehH00000NkvXXu0mjfcAGp5 diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/view-comments.png b/3.0/themes/greydragon/css/colorpacks/wind/images/view-comments.png deleted file mode 100644 index f33bdf1952832e6e119e24fcf3589d040bdef66b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4NyWWz$e7DaN_pUr%zwL zeEG_iD|hbPdGzSflP6D}K7IQ9`SaJWU%!3(_T9U8pFe;8^5si$(vdQtF3tjv$YKTt zzC$3)D5~Mr02Gugag8Vm&QB{TPb^Ah2uRG#E79|F4N)jF)-%;JvFkkp(pl!|;us=v zIXS_BX-|3L119G0@9yqybaKdIW`2H#lSjc(;Os@lz|w__8yj^W>M=`fQZ?AX=CE}W zn}LC*!_k={5)!Nn9`!KsoH@2fykXHE@g81@h!~jzY>b&~-#&d}n(+6HLszyai@}Z+F-V^$eg37(8A5T-G@yGywqUHG%>F diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/view-full.png b/3.0/themes/greydragon/css/colorpacks/wind/images/view-full.png deleted file mode 100644 index e465d36695fad7ad03029fbd8e1b54ea4b2949e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0001) zP)t-sJ3Bi)Jv~K5MMg$OO-)TsPEJoxPf$=$QBhG+Qc_h_RaRD3S65e9SXfzESz20J zTU%RPTwGmUU44Che}8{*k++A3hlq%XkB^U#kdTp)k&=>SZpP$Ri%hS`-)YR16+}z^g;^gGy<>lq)=jZF|>+S9B?(XjM^Yird z^n|kOv;Y7A32;bRa{vGi!2kdb!2!6DYwZ9402y>eSaefwW^{L9a%BKeVQFr3E>1;M zAa*k@H7+zhjm&lc006~FL_t(2&yA4B4#F@L1Vatg^xi@M8yh$H|Bqv}J_Urtljbsq zk=|yD&&w9C>cPagJ{EDkoRJ9Q{pNJ``_-Iy80WS{>2|9*aWIeS!5FhYUkw_>z$kSn zZwd{fV5EeRXb=f=uO5un+w!8(AR5M67>fq6Fd@JMG)RDXRuAU;_y5*t_6D(8&q!LM z)gSs!qejvi`9Qe5Q6sX^mWrB^u<{8f`JsXppo9j75W_HDCf7 b00000NkvXXu0mjf1~lf( diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png b/3.0/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png deleted file mode 100644 index 58b3e0b471b8503f066676fa3242893124462559..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!3-qjE#*>x6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZP~e_=LC?PTW3y`R>!F zPoFt+=JMssSFT*SbLY z*ARs=V?9$n6T99sWk5w`o-U3d8t30$ILLQMLBKW8O=nvWx09AbaXS(L4GX%26Zrqa`1y-zm94<6=Bc<&Vx_9W1EuU}h@sqKETpNo>tU!1^R zdM9`C93f@PW4aTI@6EseQtA4s##a*Oz2f{@^8ea@+unbdA>@JJ+=dsLpMWl4@O1Ta JS?83{1OQ!uo!0;W diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/view-info.png b/3.0/themes/greydragon/css/colorpacks/wind/images/view-info.png deleted file mode 100644 index 2cc7a68ea0d6970ddc06d7bcfb26ab9d42ef8de7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7DxMN}A#O>>L zpE`Z|^ySN!uUxru=gyr+j~+dF^5p5$r_Y~1fBpLP+qZAuy?gih^XD&Lz8Lv%@B{U7 z7I;J!GcfQS0Aa?gYn_}xLCF%=h?3y^w370~qEv=}#LT=BJwMkFg)(D3Q#}*A-ZN!D zMN>Ro977~7Up=>x^N@o`!^8jcuCw_poVq||ZmaK{BaAHJMU!}v9CY3kE!q0fHOa-G zJY98H(|wltohT>s1K Zn3G!?+Ef3|mH@hs!PC{xWt~$(698=Do3Q`@ diff --git a/3.0/themes/greydragon/css/colorpacks/wind/images/view-left.png b/3.0/themes/greydragon/css/colorpacks/wind/images/view-left.png deleted file mode 100644 index b51e336884659567c903a75ee1d435cdebef3eba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 539 zcmV+$0_6RPP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0001) zP)t-sJ3Bi)Jv~K5MMg$OO-)TsPEJoxPf$=$QBhG+Qc_h_RaRD3S65e9SXfzESz20J zTU%RPTwGmUU44Che}8{*k++A3hlq%XkB^U#kdTp)k&=>SZpP$Ri%hS`-)YR16+}z^g;^gGy<>lq)=jZF|>+S9B?(XjM^Yird z^n|kOv;Y7A32;bRa{vGi!2kdb!2!6DYwZ9402y>eSaefwW^{L9a%BKeVQFr3E>1;M zAa*k@H7+zhjm&lc0075HL_t(2&yA3=4udcZM9)dYR*40=lmUsQo%;XZ!PJiJz|?+0 zoSs931gH{;-g0&>_DO@y3Fy|sD?l+-uAVe@2H>D7A{q+HR|!;70y5^KaIDop2Dt~2 z^wD@W%qK>^ilGoo4^}?>4auJ)-WIMS@878c=nh8p9-@NzeHQ$B)i=m zj-@eHZWvYX#=kV&E)5Z<1Ctn*G^nJ9>dlzb;9i?FcsFTKaXm-rmo#jdr|Q)>mWDK! d^004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0001) zP)t-sJ3Bi)Jv~K5MMg$OO-)TsPEJoxPf$=$QBhG+Qc_h_RaRD3S65e9SXfzESz20J zTU%RPTwGmUU44Che}8{*k++A3hlq%XkB^U#kdTp)k&=>SZpP$Ri%hS`-)YR16+}z^g;^gGy<>lq)=jZF|>+S9B?(XjM^Yird z^n|kOv;Y7A32;bRa{vGi!2kdb!2!6DYwZ9402y>eSaefwW^{L9a%BKeVQFr3E>1;M zAa*k@H7+zhjm&lc007NNL_t(2&yA41P6I&@gFUmS>mt!4X-I*TCRBO<=b(ZT3MzO2 zv|jAVk4RFGJAK-d<@L=tmbiK*Y%YcO3B)J+rkL{qNRU%R5rVok^kNt(!#RYkG${|t z^Tob4fIW%cl~K&1OBv&o^Hb*}ZJ{v?F`9D%-|Q=+zlL*KQ$`_MYw|SD(+mpdtn_Kt zwOHKjp17}#7jj?Bx&5z=Jhve$O++5scqN~Wl&dy$Uv2!&8FhGzCyimGa%a5M jeQm_@@p7+^+ctgysS004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00024 zP)t-sTy~Ucft_!Ko^XeubBLgEh@p3krFo90ev+zxldEx&w}Oj$IH;q)Y;P0)6~?|($(75+1uLN+uYpT*xcaW-{9Qe+9|9?e6aG^Yioc^z^e>p~e6J010qNS#tmY4#5Bb4#5Gqk!$S$000?u zMObuGZ)S9NVRB^vP+@6qbS_RsR3LUUE;TMRK8?(F0001_NklG_HmA)_#vW8YlP0EEpSO{4`ODPxgZm}9#jVz(p z1~qIng8{$I33_kYEhE2%pYRQxq}cc+;tl=Ca2oN1e&qoDrNMu(KPmPD4?kHU47is& P00000NkvXXu0mjf6khul diff --git a/3.0/themes/greydragon/css/forms.css b/3.0/themes/greydragon/css/forms.css deleted file mode 100644 index ab246779..00000000 --- a/3.0/themes/greydragon/css/forms.css +++ /dev/null @@ -1,107 +0,0 @@ -/** - * 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/3.0/themes/greydragon/css/layout.css b/3.0/themes/greydragon/css/layout.css deleted file mode 100644 index db55e4af..00000000 --- a/3.0/themes/greydragon/css/layout.css +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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/3.0/themes/greydragon/css/menus.css b/3.0/themes/greydragon/css/menus.css deleted file mode 100644 index 3ba173f2..00000000 --- a/3.0/themes/greydragon/css/menus.css +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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/3.0/themes/greydragon/css/modules.css b/3.0/themes/greydragon/css/modules.css deleted file mode 100644 index 371434f8..00000000 --- a/3.0/themes/greydragon/css/modules.css +++ /dev/null @@ -1,135 +0,0 @@ -/** - * 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/3.0/themes/greydragon/css/old_ie.css b/3.0/themes/greydragon/css/old_ie.css deleted file mode 100644 index 9a5da7b8..00000000 --- a/3.0/themes/greydragon/css/old_ie.css +++ /dev/null @@ -1,16 +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/3.0/themes/greydragon/css/screen.css b/3.0/themes/greydragon/css/screen.css deleted file mode 100644 index 17ecf082..00000000 --- a/3.0/themes/greydragon/css/screen.css +++ /dev/null @@ -1,224 +0,0 @@ -/** - * 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/3.0/themes/greydragon/helpers/exif_event.php b/3.0/themes/greydragon/helpers/exif_event.php deleted file mode 100644 index 27b617a6..00000000 --- a/3.0/themes/greydragon/helpers/exif_event.php +++ /dev/null @@ -1,43 +0,0 @@ -is_album()) { - exif::extract($item); - } - } - - static function item_deleted($item) { - db::build() - ->delete("exif_records") - ->where("item_id", "=", $item->id) - ->execute(); - } - - static function photo_menu($menu, $theme) { - $item = $theme->item(); - $menu->append( - Menu::factory("link") - ->id("exifdata-link") - ->label(t("Photo Details")) - ->url(url::site("exif/show/$item->id")) - ->css_id("g-exifdata-link")); - } -} diff --git a/3.0/themes/greydragon/helpers/greydragon_event.php b/3.0/themes/greydragon/helpers/greydragon_event.php deleted file mode 100644 index 6f84512d..00000000 --- a/3.0/themes/greydragon/helpers/greydragon_event.php +++ /dev/null @@ -1,41 +0,0 @@ -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/3.0/themes/greydragon/helpers/greydragon_installer.php b/3.0/themes/greydragon/helpers/greydragon_installer.php deleted file mode 100644 index 461e6914..00000000 --- a/3.0/themes/greydragon/helpers/greydragon_installer.php +++ /dev/null @@ -1,30 +0,0 @@ - \ No newline at end of file diff --git a/3.0/themes/greydragon/helpers/greydragon_theme.php b/3.0/themes/greydragon/helpers/greydragon_theme.php deleted file mode 100644 index 988da98c..00000000 --- a/3.0/themes/greydragon/helpers/greydragon_theme.php +++ /dev/null @@ -1,30 +0,0 @@ -' - . $theme_info->name . ' ' . $theme_info->version . ''; - } -} - diff --git a/3.0/themes/greydragon/images/avatar.jpg b/3.0/themes/greydragon/images/avatar.jpg deleted file mode 100644 index 71166cc42e6ea4a50267bc949ee3bb7efc820c5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1442 zcmex=kx|JhscGpMnOVgprDf$6 zl~v6xt!?ccon4bAPnkMx`iz;g7A;<~blLJ1D_3pWyk+aQ?K^hvI&}ER(PPI?oIG{u z@|COCuHU$M>*1rvPo6$|{^I4UkDoq&`TFhqkDtFl{$gZch6fo|e+dHp#l*tG%)$=x z7b88f2KE_o z9%~}YXK;@p{B?_ghnW!=dCYA@qsIqS2QR&ml z6IRM(ym@!LVZ|!R8IvzCcz#(o>)MIETX(;eH{H9t#QrEt`mgJ||L}jfQeJg!<;wFi zdAn*SIZFw6bLQQ*zLG4j<=XQkVV;HM0{;2WK7@DbzS+M;{bDJ{T63c<{rO6}rb?zw zV*dT(0{_oU6>rHm$9Dc_*d%zRt~Y*pyJzS0%qAPT9S;1ql`_xoU)vY`a&+g~P&Y3AFzAtxM;pLy}X@VF{l<;ey7GgY=5F0A({v%SIhCotA-VbbZ; zg5T^XC6WqnM*6SM(qP_m>C4|=aWj6`{hOO~Kla*1nSWc~&1YHKb^6G5tI{L$LaNrz z^a$LbJn0?VeW^MB86*Jj^^FQSz)rF`YR6+?uMY@Nam%=G87um6$# zu>WY(vX#s39$)Ui{I+vxe(AT<6F$^T^q3?pl;ExQm4$cpmgbX38EWNjMJT^x>~}n8 zyKn#Xr8ToZu8YY3e($`juy?;fOy1*Z zw|gGX`}6g0<@&BY$saBsmC9SaGWP89JMp*9mfIfKyDe;v#FMFWeO56}GHPu4^MH?m zrO`p&Bs;3^ta@PW{-SebFaKUq{}mW}yz|Sqc86%VAK4GdN+0;;`n|Iz{=sc|0n@d;o1?RK{hP@8bcxul+$o8?d%kix&;2cOs7v{U)P=Xm1QJ=KfR%`aTq?af%!U=#d!dgni$*Z&!K z|3sL~m#onHenjx4cu}Uo%U$257S diff --git a/3.0/themes/greydragon/images/blue-grad.png b/3.0/themes/greydragon/images/blue-grad.png deleted file mode 100644 index 36e0f6bc25b10ca27ca968108563518fe5dc49db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^tUxT!!2~21I85*dQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jikv)M978H@CDnX=Db6gEkmSIs^Z()xp8pr0{(pS^|NIG+ z|398e{=m6H{efN6yPy6J_J`|F-ErUaKah395q{5i%p3*`AMWWWCU;$$4>XOz)78&q Iol`;+0KbGg>;M1& diff --git a/3.0/themes/greydragon/images/button-grad-active-vs.png b/3.0/themes/greydragon/images/button-grad-active-vs.png deleted file mode 100644 index dc641725f90ae1c2600aa73f61fd71852de7b2ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^tUxTs!2~3;Wt?9DDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MOL0Jjv*Ddl1k3aJ<`B3Ln45YTUl8-&W@Q`+4B|WrDSr z1<%~X^wgl##FWaylc_d9MYf(Ujv*Ddl6qdpoNr)>`1k*R|C#h82i6y#Bx=~&{;RB1 zKk>gk{-1o?zxohU`MZ1SMP`4WKkZ?sTs#9K8^fQ=ep~#F0-b>dF?hQAxvXra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%H3s;Exc(n#urXyR3(zrIB|(0{ z49sktf^te)y2h3+9-&dOX+`CY9TO%`o4IJ|s@2-2W!cm@VW zW=|K#5RLOkC&fk|R^Vwhb~Q`M=)LRmTH^50{y+caGnjIhhueL2I`7KQUXgi%`M`e7 zo%ajh)ZZv54t!f?n08rl*0IPVer=m)UkokZ6I^h(U-!1t$_c@G0iGc`5vMwh8Yai6 zgzLtO-1%^O*HgvQoh!rp7X4A;Z*c9C)|ujW?JJ6=mowip5roe zIk#Sa&7o&?8q3$k%Xey499y;1*#FG$)0!f4zXw`G_fJr&yNn{1`T?2eVT!WkTr!?&^Y1$v$bU3)_cyQn8;92KVmR%~@c&)W>f9tIM(?IBO z>)vbAHeOn`?C|!D$G7i2J?-SRWhbv~KY4EZ$xGW$UR$uOAQEUGXMsm#F#`kNVGw3K zp1&dmC@5Lt8c`CQpH@L&ZCWNx zGxted(ER#$isbZTELLr{Gh`;dx$w>LzPG$h(aYj*XN$J`r>;4Zvw>6Ql$Cw$U%L}u p_ob@+p0?B>HSfmBFURXYFiNK=n!R29=seKn44$rjF6*2UngBBZs^b6v diff --git a/3.0/themes/greydragon/images/favicon.ico b/3.0/themes/greydragon/images/favicon.ico deleted file mode 100644 index 66531d8e01618d1fc7b58f386607ea67619c7da8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmcJL?@Lor7{`wo1og(SdeMvQT|v+v(2L&px|aq)f(e0<^`@UnQNAc4!^+=NXriOU z6vEY@EoYkIM!6qucW!syd(Zvh)$Pu$xe~vfdu3ruqKMArd!F+==YGD=Ifsxk_El66 zHXk8%y9hZ*2sy^YO02R~XTchkdkHc1zlM-ZoLkYCFK04q`~S8_DwRH=BqFO?lEV_8 zm{KUcV;IJsKk>=u^CvYSt_!XnL`EJVD~7Qgo`NdGBQm8mzw;;*3WrsJ*Xg7KzLsiC zKWnh~xt6n7a&|!D{E$UqTve2#r9SL@`=mrHq_Ji8@@Vx9xg*ItNj&d{G+2T4! zW7pFQ9*E91h^}tSN1q#I=V2a!uWnbKHDDK}HWcbn#$F1~0sJq+_s z=DD=#A>_5>SSeGQr#aslBRT~mHXF){(F0i_QpMhRG`keYtgotvO8?&}&&i8*-?Hh` z+xM~(cYJ&PuWO*40EDEO_(nq9RfII{X0GL1&P^=1%h(>%I;(9kZC)T2V{+dCIgajE diff --git a/3.0/themes/greydragon/images/missing-img.png b/3.0/themes/greydragon/images/missing-img.png deleted file mode 100644 index 12b7394fcd832ee62201853240fb4a15a39bd793..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33136 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 diff --git a/3.0/themes/greydragon/js/ui.support.js b/3.0/themes/greydragon/js/ui.support.js deleted file mode 100644 index 353a30cc..00000000 --- a/3.0/themes/greydragon/js/ui.support.js +++ /dev/null @@ -1,156 +0,0 @@ -/* -* Grey Dragon Theme: JS support -*/ - -jQuery.fn.extend({ - myAjaxLoginSubmit: function() { - - var myAjaxLoginSubmitOps = { - dataType: 'json', - success: function(data) { - if (data.result == 'error') { - $('#g-login').html(data.form); - $().myAjaxLoginSubmit(); - } else { - Shadowbox.close(); - window.location.reload(); - } - } - }; - - $('form#g-login-form').one('submit', function() { - $(this).ajaxSubmit(myAjaxLoginSubmitOps); - return false; - }); - }, - - myAjaxSubmit: function() { - - var myAjaxSubmitOps = { - dataType: 'json', - success: function(data) { - if (data.result == 'error') { - $('#sb-content form').html(data.form); - $().myAjaxSubmit(); - } else { - Shadowbox.close(); - window.location.reload(); - } - } - }; - - $('form').one('submit', function() { - $(this).ajaxSubmit(myAjaxSubmitOps); - return false; - }); - }, - -/* - _ajaxify_dialog: function() { - var self = this; - $("#g-dialog form").ajaxForm({ - dataType: "json", - beforeSubmit: function(formData, form, options) { - form.find(":submit") - .addClass("ui-state-disabled") - .attr("disabled", "disabled"); - return true; - }, - success: function(data) { - if (data.form) { - var formData = unescape(data.form); - $("#g-dialog form").replaceWith(formData); - $("#g-dialog form :submit").removeClass("ui-state-disabled") - .attr("disabled", null); - self._ajaxify_dialog(); - self.form_loaded(null, $("#g-dialog form")); - if (typeof data.reset == 'function') { - eval(data.reset + '()'); - } - } - if (data.result == "success") { - if (data.location) { - window.location = data.location; - } else { - window.location.reload(); - } - } - } - }); -*/ - theme_ready: function() { - try { - Shadowbox.setup("a.g-fullsize-link", {player: 'img'}); - Shadowbox.setup("a.g-sb-preview", {player: 'img', gallery: "preview", animate: false, continuous: true, counterType: "skip", animSequence: "wh", slideshowDelay: 5 }); - - Shadowbox.setup(".g-dialog-link", {player: 'ajax', width: 500, height: 420, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup("a#g-login-link", {player: 'ajax', width: 340, height: 190, enableKeys: false, animate: false, onFinish: $().myAjaxLoginSubmit}); - Shadowbox.setup("a#g-exifdata-link", {player: 'ajax', width: 600, height: 420, animate: false}); - Shadowbox.setup("a#g-disclaimer", {player: 'ajax', width: 600, height: 420}); - - Shadowbox.setup("#g-site-menu .ui-icon-pencil", {player: 'ajax', width: 500, height: 420, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-pencil", {player: 'ajax', width: 500, height: 420, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu .ui-icon-plus", {player: 'ajax', width: 500, height: 390, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-plus", {player: 'ajax', width: 500, height: 390, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu .ui-icon-note", {player: 'ajax', width: 500, height: 370, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-note", {player: 'ajax', width: 500, height: 370, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu .ui-icon-key", {player: 'ajax', width: 700, height: 300, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-key", {player: 'ajax', width: 700, height: 300, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu #g-menu-organize-link", {player: 'ajax', width: 710, height: 460, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu #g-menu-organize-link",{player: 'ajax', width: 710, height: 460, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup(".g-context-menu .ui-icon-folder-open", {player: 'ajax', width: 400, height: 380, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup("#g-site-menu .g-quick-delete", {player: 'ajax', width: 400, height: 150, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-trash", {player: 'ajax', width: 400, height: 150, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-user-profile .g-dialog-link", {player: 'ajax', width: 500, height: 280, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#add_to_basket .g-dialog-link", {player: 'ajax', width: 500, height: 360, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - } catch (e) { } - - try { - $(".g-message-block").fadeOut(10000); - $(".g-context-menu .g-ajax-link").gallery_ajax(); - } catch (e) { } - - $("#g-site-menu>ul>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/3.0/themes/greydragon/libraries/MY_Theme_View.php b/3.0/themes/greydragon/libraries/MY_Theme_View.php deleted file mode 100644 index 4f579ec2..00000000 --- a/3.0/themes/greydragon/libraries/MY_Theme_View.php +++ /dev/null @@ -1,313 +0,0 @@ -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/3.0/themes/greydragon/theme.info b/3.0/themes/greydragon/theme.info deleted file mode 100644 index cea1d8d0..00000000 --- a/3.0/themes/greydragon/theme.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Grey Dragon Theme" -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/3.0/themes/greydragon/thumbnail.png b/3.0/themes/greydragon/thumbnail.png deleted file mode 100644 index 4b80ecaf35a50ced69c426d9c6b9e16b8af6a322..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25791 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 O0000 -
    - 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/3.0/themes/greydragon/views/block.html.php b/3.0/themes/greydragon/views/block.html.php deleted file mode 100644 index af29546f..00000000 --- a/3.0/themes/greydragon/views/block.html.php +++ /dev/null @@ -1,33 +0,0 @@ - - - - -
    - is_blockheader_visible): ?> -

    - -
    - -
    -
    diff --git a/3.0/themes/greydragon/views/dynamic.html.php b/3.0/themes/greydragon/views/dynamic.html.php deleted file mode 100644 index 1f787cf3..00000000 --- a/3.0/themes/greydragon/views/dynamic.html.php +++ /dev/null @@ -1,39 +0,0 @@ - -
    -
    - dynamic_top() ?> -
    -

    -
    - -add_paginator("top"); ?> - -
      - $child): ?> - get_thumb_element($child) ?> - -
    -dynamic_bottom() ?> - -add_paginator("bottom"); ?> diff --git a/3.0/themes/greydragon/views/exif_sidebar.html.php b/3.0/themes/greydragon/views/exif_sidebar.html.php deleted file mode 100644 index 115bfc86..00000000 --- a/3.0/themes/greydragon/views/exif_sidebar.html.php +++ /dev/null @@ -1 +0,0 @@ - diff --git a/3.0/themes/greydragon/views/info_block.html.php b/3.0/themes/greydragon/views/info_block.html.php deleted file mode 100644 index d3860584..00000000 --- a/3.0/themes/greydragon/views/info_block.html.php +++ /dev/null @@ -1,24 +0,0 @@ - -
      - owner): ?> -
    • - - owner->url): ?> - owner->display_name()) ?> - - owner->display_name()) ?> - -
    • - - captured): ?> -
    • - - captured)?> -
    • - - description): ?> -
    • - bb2html(html::purify($item->description), 1) ?> -
    • - -
    diff --git a/3.0/themes/greydragon/views/login_ajax.html.php b/3.0/themes/greydragon/views/login_ajax.html.php deleted file mode 100644 index 76028c4e..00000000 --- a/3.0/themes/greydragon/views/login_ajax.html.php +++ /dev/null @@ -1,41 +0,0 @@ - - - -
    - - - - -
    - diff --git a/3.0/themes/greydragon/views/movie.html.php b/3.0/themes/greydragon/views/movie.html.php deleted file mode 100644 index ec870608..00000000 --- a/3.0/themes/greydragon/views/movie.html.php +++ /dev/null @@ -1,43 +0,0 @@ - -
    - 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/3.0/themes/greydragon/views/no_sidebar.html.php b/3.0/themes/greydragon/views/no_sidebar.html.php deleted file mode 100644 index dd61bb77..00000000 --- a/3.0/themes/greydragon/views/no_sidebar.html.php +++ /dev/null @@ -1,24 +0,0 @@ - -  ?> - diff --git a/3.0/themes/greydragon/views/page.html.php b/3.0/themes/greydragon/views/page.html.php deleted file mode 100644 index 13664d65..00000000 --- a/3.0/themes/greydragon/views/page.html.php +++ /dev/null @@ -1,147 +0,0 @@ - - -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; -?> - - -"; ?> - - - -item()): ?> -item()->is_album()): ?> - $theme->bb2html($theme->item()->title, 2))) ?> -item()->is_photo()): ?> - $theme->bb2html($theme->item()->title, 2))) ?> - - $theme->bb2html($theme->item()->title, 2))) ?> - -tag()): ?> - $theme->bb2html($theme->tag()->name, 2))) ?> - - - - -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" /> - - - - - - - -page_top() ?> -
    - header_top() ?> - - - - - - -guest) or ($theme->show_guest_menu)): ?> -
    "> - site_menu() ?> -
    - - messages() ?> -header_bottom() ?> - -loginmenu_position == "header"): ?> - user_menu() ?> - - -show_breadcrumbs): ?> - breadcrumb_menu($theme, $parents); ?> - -
    -
    -
    - sidebar_menu($url) ?> -
    "> - - album_menu() ?> - - photo_menu() ?> - - movie_menu() ?> - - tag_menu() ?> - -
    - - sidebarvisible=="left"): ?> - ' ?> - sidebarvisible=="none"): ?> - - ' ?> - - - page_subtype != "login") and ($theme->page_subtype != "reauthenticate") and ($theme->sidebarvisible != "none")): ?> - - - sidebarvisible != "none")? "
    " : null ?> - - sidebarvisible == "left"): ?> - ' ?> - sidebarvisible == "none"): ?> - ' ?> - - ' ?> - - -
    - - - -page_bottom() ?> - - diff --git a/3.0/themes/greydragon/views/paginator.html.php b/3.0/themes/greydragon/views/paginator.html.php deleted file mode 100644 index 9b5e725e..00000000 --- a/3.0/themes/greydragon/views/paginator.html.php +++ /dev/null @@ -1,188 +0,0 @@ - - - -parent(); - endif; - $current_page = $page; - $total_pages = $max_pages; - // Prepare page url list - 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++): - $_pagelist[$i] = $siblings[$i-1]->url(); - endfor; - break; - default: - $current_page = 1; - $total_pages = 1; - $_pagelist[1] = url::site(); - break; - } - - if ($total_pages <= 1): - $pagination_msg = " "; - else: - $pagination_msg = t("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) . ''; - 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) . ''; - endif; - if ($i < 10): - $pagination_msg .= '·'; - endif; - endfor; - - $pagination_msg .= '…'; - $pagination_msg .= '' . t($total_pages - 1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t($total_pages) . ''; - - elseif ($current_page > $total_pages - 8): - $pagination_msg .= '' . t(1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; - $pagination_msg .= '…'; - - for ($i = $total_pages - 9; $i <= $total_pages; $i++): - if ($i == $current_page): - $pagination_msg .= '' . t($i) . ''; - else: - $pagination_msg .= '' . t($i) . ''; - endif; - if ($i < $total_pages): - $pagination_msg .= '·'; - endif; - endfor; - - else: - $pagination_msg .= '' . t(1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; - $pagination_msg .= '…'; - - for ($i = $current_page - 5; $i <= $current_page + 5; $i++): - if ($i == $current_page): - $pagination_msg .= '' . t($i) . ''; - 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 .= '·'; - $pagination_msg .= '' . t($total_pages) . ''; - endif; - endif; -?> - - \ No newline at end of file diff --git a/3.0/themes/greydragon/views/photo.html.php b/3.0/themes/greydragon/views/photo.html.php deleted file mode 100644 index 884e30a5..00000000 --- a/3.0/themes/greydragon/views/photo.html.php +++ /dev/null @@ -1,79 +0,0 @@ - -desc_allowbbcode): ?> - bb2html($item->description, 1); ?> - - description)); ?> - - -is_photometa_visible): ?> -' . $theme->thumb_info($item) . ''; ?> - - -
    - 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/3.0/themes/greydragon/views/rss_block.html.php b/3.0/themes/greydragon/views/rss_block.html.php deleted file mode 100644 index 4d30ce59..00000000 --- a/3.0/themes/greydragon/views/rss_block.html.php +++ /dev/null @@ -1,13 +0,0 @@ - - diff --git a/3.0/themes/greydragon/views/search.html.php b/3.0/themes/greydragon/views/search.html.php deleted file mode 100644 index 94fc170c..00000000 --- a/3.0/themes/greydragon/views/search.html.php +++ /dev/null @@ -1,43 +0,0 @@ - -
    -

    $q)) ?>

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

     

    -

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

    - - -
    \ No newline at end of file diff --git a/3.0/themes/greydragon/views/sidebar.html.php b/3.0/themes/greydragon/views/sidebar.html.php deleted file mode 100644 index 0cad333d..00000000 --- a/3.0/themes/greydragon/views/sidebar.html.php +++ /dev/null @@ -1,8 +0,0 @@ - - -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/3.0/themes/greydragon/views/tag_block.html.php b/3.0/themes/greydragon/views/tag_block.html.php deleted file mode 100644 index f9bc5886..00000000 --- a/3.0/themes/greydragon/views/tag_block.html.php +++ /dev/null @@ -1,27 +0,0 @@ - - -
    "> - -
    - \ No newline at end of file diff --git a/3.0/themes/greydragon/views/user_profile.html.php b/3.0/themes/greydragon/views/user_profile.html.php deleted file mode 100644 index b7d92f40..00000000 --- a/3.0/themes/greydragon/views/user_profile.html.php +++ /dev/null @@ -1,50 +0,0 @@ - - - -
    -

    $user->display_name())) ?>

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

    title) ?>

    -
    - view ?> -
    -
    - -
    diff --git a/3.1/modules/kbd_nav/changelog.txt b/3.1/modules/kbd_nav/changelog.txt deleted file mode 100644 index 203fdea1..00000000 --- a/3.1/modules/kbd_nav/changelog.txt +++ /dev/null @@ -1,20 +0,0 @@ -Kbd Navigation Changelog - -version 1.5: -- Fix for RTL detection -- Added support for Wind theme - -version 1.4: -- Added RTL detection - -version 1.3: -- Internal revision - -version 1.2: -- Added support for GreyDragon Photo Slideshow navigation - in Photo SB slideshow mode, key navigation is superseded by slideshow navigation. - -version 1.1: -- Internal revision - -version 1.0: -- Initial release \ No newline at end of file diff --git a/3.1/modules/kbd_nav/helpers/kbd_nav_theme.php b/3.1/modules/kbd_nav/helpers/kbd_nav_theme.php deleted file mode 100644 index e085f187..00000000 --- a/3.1/modules/kbd_nav/helpers/kbd_nav_theme.php +++ /dev/null @@ -1,8 +0,0 @@ -script("kbd_nav.js"); - } -} \ No newline at end of file diff --git a/3.1/modules/kbd_nav/js/kbd_nav.js b/3.1/modules/kbd_nav/js/kbd_nav.js deleted file mode 100644 index 25eb7210..00000000 --- a/3.1/modules/kbd_nav/js/kbd_nav.js +++ /dev/null @@ -1,102 +0,0 @@ -/** -* -* Copyright (c) 2010 Serguei Dosyukov, http://blog.dragonsoft.us -* -* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation -* files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, -* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*/ - -$.fn.KbdNavigation = function(options, callback) { - - this.options = options || {}; - var opt = this.options; - this.callback = callback || null; - var clbk = this.callback; - - $(this).bind("keydown", function(event) { - if ($('#sb-body-inner>img#sb-content').is(':visible')) { - return false; - } - // ignore shortcuts when inside a jQuery dialog; otherwise it becomes impossible - // to navigate the cursor inside an input box - if ($('.ui-widget-overlay').is(':visible')) { - return true; - } - - var direction = "ltr"; - if (document.body) { - if (window.getComputedStyle) { - direction = window.getComputedStyle(document.body, null).direction; - } else if (document.body.currentStyle) { - direction = document.body.currentStyle.direction; - } - } - - var lnk = ""; - var lnk_first, lnk_prev, lnk_parent, lnk_next, lnk_last; - - if(opt.first) { lnk_first = opt.first; } else { lnk_first = $("#g-navi-first").attr("href"); } - if(opt.prev) { lnk_prev = opt.prev; } else { lnk_prev = $("#g-navi-prev").attr("href"); } - if(opt.parent) { lnk_parent = opt.parent; } else { lnk_parent = $("#g-navi-parent").attr("href"); } - if(opt.next) { lnk_next = opt.next; } else { lnk_next = $("#g-navi-next").attr("href"); } - if(opt.last) { lnk_last = opt.last; } else { lnk_last = $("#g-navi-last").attr("href"); } - - // Support for standard Wind Theme tags - if(!lnk_first) { lnk_first = $(".g-paginator .ui-icon-seek-first").parent().attr("href"); } - if(!lnk_prev) { lnk_prev = $(".g-paginator .ui-icon-seek-prev").parent().attr("href"); } - if(!lnk_next) { lnk_next = $(".g-paginator .ui-icon-seek-next").parent().attr("href"); } - if(!lnk_last) { lnk_last = $(".g-paginator .ui-icon-seek-end").parent().attr("href"); } - - var keyCode = event.keyCode; - - if (direction == "rtl") { - switch(keyCode) { - case 0x25: // Left - keyCode = 0x27; - break; - case 0x27: // Right - keyCode = 0x25; - break; - } - } - - switch(keyCode) { - case 0x25: // Ctr+Left/Left - if(event.ctrlKey) { lnk = lnk_first; } else { lnk = lnk_prev; } - break; - case 0x26: // Ctrl+Up - if(event.ctrlKey) { lnk = lnk_parent; } - break; - case 0x27: // Ctrl+Right/Right - if(event.ctrlKey) { lnk = lnk_last; } else { lnk = lnk_next; } - break; - } - - if(lnk) { - if(typeof clbk == 'function') { - clbk(); - return false; - } else { - window.location = lnk; - return true; - } - } - - return true; - }); -} - -$(document).ready( function() { - $(document).KbdNavigation({}); - if ($('#sb-content').is(':visible')) { return true; } -}); diff --git a/3.1/modules/kbd_nav/module.info b/3.1/modules/kbd_nav/module.info deleted file mode 100644 index 2eda7c73..00000000 --- a/3.1/modules/kbd_nav/module.info +++ /dev/null @@ -1,3 +0,0 @@ -name = "Kbd Navigation" -description = "Adds keyboard navigation to the gallery.
    Version 1.5 | By Serguei Dosyukov | Visit plugin Site | Support" -version = 5 diff --git a/3.1/themes/greydragon/admin/controllers/admin_theme_options.php b/3.1/themes/greydragon/admin/controllers/admin_theme_options.php deleted file mode 100644 index ec33e6b3..00000000 --- a/3.1/themes/greydragon/admin/controllers/admin_theme_options.php +++ /dev/null @@ -1,469 +0,0 @@ -load_theme_info(); - return ($theme_info->version); - } - - 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->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); - - $form->submit("g-theme-options-save")->value(t("Save Changes")); - - return $form; - } - - 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()): - 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"); - - 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); - - 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"); - - module::clear_var("th_greydragon", "sidebar_albumonly"); - module::clear_var("th_greydragon", "sidebar_allowed"); - module::clear_var("th_greydragon", "sidebar_visible"); - - module::event("theme_edit_form_completed", $form); - message::success(t("Theme details are reset")); - else: - // * General Settings **************************************************** - - $_priorratio = module::get_var("th_greydragon", "thumb_ratio"); - if (!$_priorratio): - $_priorratio = "digital"; - endif; - - $resize_size = $form->edit_theme->resize_size->value; - $thumb_size = 200; - - $build_resize = $form->maintenance->build_resize->value; - $build_thumbs = $form->maintenance->build_thumbs->value; - $build_exif = $form->maintenance->build_exif->value; - - $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; - - 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; - - 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; - - if ($build_exif): - db::build() - ->delete("exif_records") - ->execute(); - endif; - - 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; - - url::redirect("admin/theme_options"); - 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/3.1/themes/greydragon/admin/views/admin_theme_options.html.php b/3.1/themes/greydragon/admin/views/admin_theme_options.html.php deleted file mode 100644 index 6c919461..00000000 --- a/3.1/themes/greydragon/admin/views/admin_theme_options.html.php +++ /dev/null @@ -1,59 +0,0 @@ - - - - -
    -
    -
    -
    - -
    -
    - -
    -
    - -
    -
    diff --git a/3.1/themes/greydragon/changelog.txt b/3.1/themes/greydragon/changelog.txt deleted file mode 100644 index 3d5a1451..00000000 --- a/3.1/themes/greydragon/changelog.txt +++ /dev/null @@ -1,239 +0,0 @@ -=== 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/3.1/themes/greydragon/controllers/greydragon.php b/3.1/themes/greydragon/controllers/greydragon.php deleted file mode 100644 index d6e51c8c..00000000 --- a/3.1/themes/greydragon/controllers/greydragon.php +++ /dev/null @@ -1,39 +0,0 @@ -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/3.1/themes/greydragon/css/colorpacks/carbon/colors.css b/3.1/themes/greydragon/css/colorpacks/carbon/colors.css deleted file mode 100644 index 57fd30dd..00000000 --- a/3.1/themes/greydragon/css/colorpacks/carbon/colors.css +++ /dev/null @@ -1,192 +0,0 @@ -/** - * 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/3.1/themes/greydragon/css/colorpacks/carbon/images/ajax-loading.gif b/3.1/themes/greydragon/css/colorpacks/carbon/images/ajax-loading.gif deleted file mode 100644 index 0996045a0978d28e0ac2fb83a634bc349cfab407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4782 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 diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-album.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-album.png deleted file mode 100644 index ac87ec4fbf6acb75ac29259e60d8a9a0992cbef0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA3y|d6q_!SNv7|ftIx;Y9?C1WI$O`0h7I;J! zGca%qfiUBxyLEqng6t)pzOL*yIe{8wlsqH7fI^Zbt`Q}{`DrEPiAAXl0g0J;C3=3Y zAqr*2dZv0NcD-lHfQn9fx;TbNTrR!1-|KLKgxkmXN8((Zi^|H1i>6eu&)uW=Q7Xo| zWA@_khq6m{?A&S5q0ljHmVJDXY*3-+{iizZ%>4OGF?VN4?l$6ilYV{ip{4eW-1%{9 zO{?~1yo%H3`FQqaZ~0E+&MR(NFD2)0(BAi9lW5tI?4t@LFH{yC^V06BatS`-c;k4x z{FSJ!JUb`XKRnT>-|4e-lIDjWwbO$+1*^G^tA%OxMsf5QL( diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-error.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-error.png deleted file mode 100644 index c37bd062e60c3b38fc82e4d1f236a8ac2fae9d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&N#0$9Ug7g~-`rQ^qx~m@y2OU8A z#zh~=7n#Z$Z*fx-GOtDf07cgx0suCz_W(2~Y(0tf@FX@P6EPuM_dgn$vj9LucO)%W zw%HgMW>=#oL>nZ>M&NEf08>)#)k<{$fCT_r>rPi=BV=hFh6WS^qqze>C6Ek}o{M5% za|@JGowu0t{&hgNzySHZxy@LTNh);YzZ2zSp_ zl$^T&Dnc|NLb&RD_!4>pt@VHdP)ZGER%5ZmWEe$lryR&y;2u^3cOkO4#6c%-(EY6a{600000NkvXXu0mjfxS2AI diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-separator.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-separator.png deleted file mode 100644 index 3e158515556616fcf3cab5e837664263f6c58c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VkYHF5IUx~9v7|ftIx;Y9?C1WI$O_~$l?3?( zGcc4*K5GHwNtC!olmzFem6RtIr7{F0X6BXX`MHKDlo{(8o2`8QNEN6?(bL5-gyVX0 z!U4v#yhv;IWAnD9iq5gkd_O1j#iA2gADI~V9C;r3-B_CiRLtP%>gTe~DWM4fa9k}Q diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-success.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/ico-success.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1p004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000id000id0mpBsWB>pF8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(0m?~4K~y+Tz12-i0znuD@IBO5QheEUch$Ul5JYNkUeu%nsY5STrsXZH%sPk~ zimiH4@S+F(3|0u`C-5MiJ^XVXFtD^Xlm>=*XN{J1p>GdwyygQ}{i zR;$S8^T_3L6xV7sTqF_;(YfGmcn_sg3B_U&UauE+yB(4w!Q=5DnM|V5XyEYpcrH2@ zyt=+YrBWeGuh&!8>2xp{3~)M~G?7{^YX>u#46@lQ91aJ}W;1x6hsk83*l08&olc|I z>tQDp`i9QUuuv$_k{QUB%0NL7=*qI}bUI)y8jEQITdfvGqY+gN14U7UEXx$TTrLcU zLjryNfHv?+Ez^K*w+ls42xOSs?WVnD?d|4hCtkFAzY#->1nKdNg^z zl3JdkH>uQWK%d`_%P(d4>yrO^L=tG?Eh{@2ZDlPT6>VJ=9X(ZDeN{aJH9bQ$eIs=PV@r3Rkmy~xKrNCbt`Q}{ z`DrEPiAAXl0g0J;C3=3YAqr*2dZv1Ye$Op_0u{-7x;Tb#Tu-(*@TIYnk;A?rGeoFk jrsWKY8I7Mj5||kR?70MU=8OLTDrN9=^>bP0l+XkKClfE| diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/ui-icons.png deleted file mode 100644 index 7d1723bf552fcc1f15a617b2246558416c012cba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9312 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 M%34a5iq_%(2bpt6pa1{> diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/view-calendar.png deleted file mode 100644 index 5442fa51321e3a2a4f0d2ce50fa651316b729fc6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 329 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDVB6cUq=Rpjs4tz5?O(Krjj7P zUE~zhw+=#aM-TBZ>d2^i z96H{8s@uD9!I~`)N;AFTzp-I`lJtPuXWe0 zc6zRUUguqgqtHInx3~UCbLZz-Y)&j`m zEbxddW?0j z`Mwlz`*|=mo;l+q@?OK4ah8EhL`srqTcw;k$0X4WY6}`n9YxI?S=MkaY%pCIeqdW7 s_l<35%tYR^I6Fp5e`{dvo(%LzPTB5*?IE4FfqrE0boFyt=akR{0KG?&;s5{u diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/view-full.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/view-full.png deleted file mode 100644 index 7145fd9d49d9231cfac264af85e5664f82fbcf67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 319 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-s5C}7hYIrpO1tm*dBT9nv(@M${i&7Z^5;OBk^!!{y6v~YCO!Z9cde4*r z6*YOfIEHAPPu59vV1J>cs^f5IdUNK|_x<_qjx!n?IW-QQIC0{@p%W~}1O(LG_M_NP&)JlnCE#pg!>q>BGHyKkjSU6FrT)tJy!>mw z&$QWK-Gu}dB@?yH#_Ee_A9h)!@9@m$Ns^DFX}i=yMur{toz;c;MYjW8!{F)a=d#Wz Gp$Py1x@KVj diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/view-fullsize.png deleted file mode 100644 index ebd042373ea4517a820093cd18f823a9a20a8c03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 341 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDVB6cUq=Rpjs4tz5?O(Kg=CK) zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a1C(GY3GxeOU?`h>)&j`m zEbxddW?8eR=RLCF%=h?3y^w370~qEv=}#LT=BJwMkFg)(D3Q#}*A-ZN!D zMKe5I978nDCuG$;zFqT6?njH=WvBz2Fdc^lpJgvANs}4mnJ) zNEehi004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000ib000ib0l1NC?EnA(8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(0c%i9R7C&)02mk;PEJlFBqSOd8XzDbR8&+_Qc@-+CQ(sQWo2bpSXg9aWEmM5 zDk>^0EG#fEFf}zbH#avqIXOByIz2r-KR-V}KtMr3K|@1BL_|bKM@L9VNJ>gdOiWBq zPft`-R9aeEUS3{bUteToWMyS#XJ=<YbpP-Cc> zsi~{0tE{Z7t*x!DuCA}IuduMNv9YnTva+sxVgExy1Kf%ySu!+ zyuH1>zP`S|z`(@B#K*_S$jHda$;r;n&d<-!)z#J3*4Ee8*V)lq(=H}<;=Lxr}wEzGB4RlgYQvg0dLq|-Hp{uXa(%zSwdA|Sv0UAj}K~yNuozU4M z0$~&d@UbtsvSztR+0#%Vv}c4yK1-ABYZ1nlp=jvO_vp^ZOXupZtU0}MZ8UGjAF%800000NkvXXu0mjf@Wq7L diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/view-left.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/view-left.png deleted file mode 100644 index c59af5d00dc95a3a9e5d09523e1795d93b9ac4c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-s5C}7hYIrpO1tm*dBT9nv(@M${i&7Z^5;OBk^!!{y6v~YCO!Z9cde4*r z6?J;LIEHAPUwdIAZ?l6)^TYf8;T!Ipo!HmKE74$iX(NmF+;xXUJO$;HyacD~*`2)R z;+MCK>5AgKt=rdr+vdnHNrS7aDO^~HjmLp;NdoJ&;Nr5L$;oqM9zS6?$vElo?GI=5 zVz;sH-@B3LV?@>L>Gz)PHZQ4ER&h)C6QQ_CI6hn?A>F^Pu;|~%V~k5R+(epMs%3zV OV(@hJb6Mw<&;$VYA8Shh diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/view-right.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/view-right.png deleted file mode 100644 index 595054562d3a26ded057dca1ce7d79325d48ee14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-s5C}7hYIrpO1tm*dBT9nv(@M${i&7Z^5;OBk^!!{y6v~YCO!Z9cde4*r z6?J&JIEHAPUprwVZ-at>>-%_(m~)nI*4#P3IOp&=)tHk3ohKUSthnfOVtGZQ_`Ai6 zi>59y{ig;b?>_XKnTaoV6FL20)dHZK O7(8A5T-G@yGywoR&v1nR diff --git a/3.1/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png b/3.1/themes/greydragon/css/colorpacks/carbon/images/view-slideshow.png deleted file mode 100644 index 2fb53ad09633e73cf42669bb918e1e42aae85b2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1014 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000ic000ic0Tn1pfB*mh8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(14l_jK~zXfwUzBplV=o%`2+TGe+VRen~(qjg4+i>5()+BMqnlkEy2?8VhKT_ z&=9bUECYjSmQjuJB8qP9W_c?jP@)vv2FTbZPfJJ3HZv*M!DXMvv;kXa5qXl6`^$Z< z`<(m!o!=9qR;xLVh!LSN@A;FM(SkEho}|6Koq~dbh=oK1r>Cc5O@gq&fUAg1arTII<=0H91YqpE|Hs2$WK4m=<5C+<(YHz_T3BaH5d$g z;9)Bcz)k&wlzyhAs;CK#vXM_tD7akPhJE%AK0cL8YKDTY>wUOfE)o+HBZEcd1==jm zS5sZ0qr9*lO>r<>5Byd5~r1Bg-m6SoQFBwPOChk5^rNL2C2rSDjeR;|PnZc>eO= zsUPZEbyHfV!FcmF^NYWcl#~?tjstU&oSYn@2;up2X6<&y#~(2~GJ@a~P?f5pSA__V zjg8HjoSa~8ZkAi!H>s+s;6%bl(s0;pk6Bw=!(uV-tK|a+#Qz{4hMV-bxVR6r+8T1Q zK}^oz2jwmG!^>34#K^-RtJ&2CaI1^aJvp@x~sY@5K&3Kl+tkLqA&^Fg$L< z>pvlI$o%{~GMOy02`Lo0Qqr__hOZm0QF~U0zN!P&=?WfCEn^y*;(T=-Bcl^R_`twG zWN>myisTbEtDSnap5{sey7QfUE>m%}?FNG5Ka_H~rR9p$X5aOGewkiibKOt- z-+!@7Ax-cf62div { 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/3.1/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif b/3.1/themes/greydragon/css/colorpacks/greydragon/images/ajax-loading.gif deleted file mode 100644 index 0996045a0978d28e0ac2fb83a634bc349cfab407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4782 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 diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/background.gif b/3.1/themes/greydragon/css/colorpacks/greydragon/images/background.gif deleted file mode 100644 index b8083564eefae82ff216a242edd47c6ae4914396..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1098 zcmZ?wbhEHbWMn8|`py6WjEsy-j7&hl%*@Qf%mPHLEI`D{#>xgn>}>2DY#bcy9GvW& zTpU2e1w`DO+&r8-JX}1y+`N3;eEd8>#4o_hFUTt($SWwsCn(G(B+M@?!Y?c;07N39 zf}&!AqT+&L;zD8)!s3#`5|SbkQX-PlqEbL4BPJ~?CL=2@BPT8^F9AeyK%^ikuPCLU zB&DbY^Zrm3K=rJ$jusHv@}siUN&tEOkDZD<1Z%_ub*0z`*^ z4oD>^FEDWYXJF)z@z}87U^53V8;3!I10yqsh)%+T2aPS9qB;pDHY_~ME~xA^!((BS zYqz*j#+40)%dj3 z-Bca6uDZH9j61{hz}D5**RdZ`lJMU4<_1f~Wvz(QWpA6d7d+mV>M%X8d3S~Ei@UqO zH^%Q5P~w>8u%qB1XNR!=f&()OTaQnWkDlRE`Kk5v4E^LAdkmY?&r6x)NcdJUw_jcn zSpMR{!mY3QHzXfs6I`^dI>ya7v&(&6UzkhIeyMBS=y`$ZqUtC_^eo)S>=lh4p$0sNzxBdP7VM%xNb!1@J z*w6hZkrl{SNcITwWnidMV_;}#VPN>O+y9PAvN?3`R2K*R+^+??DzoIE^SJiOez zeB6BeJp4c;z$+lgD=5S#D9kS;%r7h=AS@~%A}S~7WUg8>25}cn_Ql40p$`Fv4 znOCCc=Nh6=W~^tr*LCqppb`^L7sn8e>&XTQzs?_NVEX^~$=~+NHzHOjaeT7x(pYt( q-bD6>!7dQBLhryUvosZXU|3CAtoLO}sOv+Zeg;ohKbLh*2~7YHl27LV diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-album.png deleted file mode 100644 index ac87ec4fbf6acb75ac29259e60d8a9a0992cbef0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^0w6XA3y|d6q_!SNv7|ftIx;Y9?C1WI$O`0h7I;J! zGca%qfiUBxyLEqng6t)pzOL*yIe{8wlsqH7fI^Zbt`Q}{`DrEPiAAXl0g0J;C3=3Y zAqr*2dZv0NcD-lHfQn9fx;TbNTrR!1-|KLKgxkmXN8((Zi^|H1i>6eu&)uW=Q7Xo| zWA@_khq6m{?A&S5q0ljHmVJDXY*3-+{iizZ%>4OGF?VN4?l$6ilYV{ip{4eW-1%{9 zO{?~1yo%H3`FQqaZ~0E+&MR(NFD2)0(BAi9lW5tI?4t@LFH{yC^V06BatS`-c;k4x z{FSJ!JUb`XKRnT>-|4e-lIDjWwbO$+1*^G^tA%OxMsf5QL( diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-error.png deleted file mode 100644 index c37bd062e60c3b38fc82e4d1f236a8ac2fae9d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&N#0$9Ug7g~-`rQ^qx~m@y2OU8A z#zh~=7n#Z$Z*fx-GOtDf07cgx0suCz_W(2~Y(0tf@FX@P6EPuM_dgn$vj9LucO)%W zw%HgMW>=#oL>nZ>M&NEf08>)#)k<{$fCT_r>rPi=BV=hFh6WS^qqze>C6Ek}o{M5% za|@JGowu0t{&hgNzySHZxy@LTNh);YzZ2zSp_ zl$^T&Dnc|NLb&RD_!4>pt@VHdP)ZGER%5ZmWEe$lryR&y;2u^3cOkO4#6c%-(EY6a{600000NkvXXu0mjfxS2AI diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-separator.png deleted file mode 100644 index 3e158515556616fcf3cab5e837664263f6c58c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VkYHF5IUx~9v7|ftIx;Y9?C1WI$O_~$l?3?( zGcc4*K5GHwNtC!olmzFem6RtIr7{F0X6BXX`MHKDlo{(8o2`8QNEN6?(bL5-gyVX0 z!U4v#yhv;IWAnD9iq5gkd_O1j#iA2gADI~V9C;r3-B_CiRLtP%>gTe~DWM4fa9k}Q diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/ico-success.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)Hs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1pPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7j#8fbW?9;ba!ELWdKlNX>N2bPDNB8b~7$Dy+xzR0009dNklz5iy(q%bSqVS0aQS#nyS!B)p~2JwYCSNwi-pf zXyQc=`WdXor1}Xw7!Pfl`10$_n5EkEpmCBXGsDjB+4;}z@<_7UiU0%)i;BNJe)5zQ z6cm#Cw5zL2^843Ma(Qu~(%dpJOO^(Abq!kLJ%|rvkW3HXi}+v$Er}j@0=1xYZe7wHCj$O}c)(ICEdVHvFX+uM6JLmm}Nvy7}a#>kf8K<9x!909cvO|5NMSXkgCGa)OL2~8#w@7XSQcX#P`0Axo- zXoltf!i9?43#ZQyS9JjOjUjArZ{zIj45?I#3q?_cEX#cC_xo{rddh{7XpCpBxz1P6 zYKh|Vz~*pq93C&0b1OWsx3>pXRk@JKg25m^Th@LY9l=T0YB{Y}D!m|fXC-92lBX(A zgT3$jJjmM4&JMP=ws3NClK+hj-j0sm$}tD4mJ3Cz4Q7iqk4>q-#>U1i0h{a}9OT94 z=jRw19fO66w5;OxP@%&OqbR{>7H=WYGTEz>$z+htX3^2v3A@9INGyiq<70j^CLN;J zwXB^UA1S9oJ$r)mwYV#hXf!XKoSNb}y-@(GmRJ=hpOoqJcZRKJ(s%ST^albwr!yE~ zt#E<}v|K@wQ9{3nhrr42ZDlPT6>VJ=9X(ZDeN{aJH9bQ$eIs=PV@r3Rkmy~xKrNCbt`Q}{ z`DrEPiAAXl0g0J;C3=3YAqr*2dZv1Ye$Op_0u{-7x;Tb#Tu-(*@TIYnk;A?rGeoFk jrsWKY8I7Mj5||kR?70MU=8OLTDrN9=^>bP0l+XkKClfE| diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/ui-icons.png deleted file mode 100644 index 7ab15cae7d3a3fe41b721abe9ec09345d1e5559c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9657 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 UeJ;!CAJ3<%sHspTZxQ~#056d*x&QzG diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-calendar.png deleted file mode 100644 index 206ccd66e043f44319ed6bf432b3c20cedb36179..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 449 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDaPU;cPEB*=VV?2IV|apzK#qG z8~eHcB(eheoCO|{#S9F5he4R}c>anMprB-lYeY$Kep*R+Vo@qXKw@TIiJqTph(ejM zo~fSkf|rq3fu}8f=?+J9cf(xHgB?!Jz)%B3Xrf zye?n0#L_qt9%?uS)$UobXNL&KDX;GEh9BE_t|??~zwEbvZRhHuXIH+JH2=896Q=OW z>;2Kp>CfiGRn+`kI9FkxXtQd^t(3h-OG97$c{1~;tgga7)n?0%U8Zku{gLL*&$HO} z?n#`$eHo=acPrYj&U#?kY0R}tYGTWKpZ)v(JbLwccK`d^wS1lx%}=G;r#5y!P8a)f zRe{sq`tP%AuV?44-&6hP38#yM`-~g6XD2fxa2z}GWXgi(84_(0!afPIhXa@vO=K2R ziD43ShD0or;$lD}j nnV@*+#-SrT5+Mg?MmPyDm^-{Uz^N|@3SkCMS3j3^P6 diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-comments.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-comments.png deleted file mode 100644 index 293c587e31161b509a6a2deb8891e03752e78a5d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 492 zcmVPx#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7j#8fbW?9;ba!ELWdKlNX>N2bPDNB8b~7$Dy+xzR0003;NklZCx3_mh19tZI4EB6jfIWi^m}VD}wekb)7-ma<{X%6$j4{RV8zF0w_?S@ z&ZX7GwX<8A7Oq&~&BtfOV9CelG;La2XtjewXmdhr_q1s~e0-J+=0JOk+Zq$X+R~G2 zLL5rl%7BhA2MP0eCKX19lqSUH1-oUYdh)@9`8?c$6Ouq6Gc%J9ANn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-sFbFdq&tH)O6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q7ALkGu1P(>pfEj zRCLzU#W6(Vd~5$%t|kYO*7K`besQQpNwIESt@9#1>(&kJ9lg`GYU^rjeG$6b_~452 z%Z-~Y0$xhMyXFI3MKMQ;^UtKr;%wmpe{)w~P hjrv=ZrR%4k6R(xZJbzz1|2NQM44$rjF6*2UngI3_j9vf$ diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-fullsize.png deleted file mode 100644 index ed76257a8cfc001029190d078470850d16cc7d2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%0VEiBdp33fDVB6cUq=Rpjs4tz5?O(Kg=CK) zUj~LMH3o);76yi2K%s^g3=E|P3=FRl7#OT(FffQ0%-I!a1C(GY3GxeOU?`h>)&j`m zEbxddW?{VmR zbkpwLuU|iVnr`&j(M7pgQILa=qgJ8s`7NDkhEg||^t+l@YK2%u=We;XCODzNfiH2A z_oRS6#;RP6@GZgo`@DaDh`AM9wJF%@%C9~5UwL0T9QpG5>gw;WS5B9x_;027NoN+D z7KHgp97eaYbI>gTe~DWM4fA=8($ diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-info.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-info.png deleted file mode 100644 index 521439ce7c35951c231bf4759dd2874fc918cffd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 938 zcmV;b16BNqP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000ic000ic0Tn1pfB*mh8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(0ew(RR7C&)02mk;PEJlFBqSOd8XzDbR8&+_Qc@-+CQ(sQWo2bpSXg9aWEmM5 zDk>^0EG#fEFf}zbH#avqIXOByIy*Z%JUl!-Jv}}?K0iM{KtMo2K|w=9LqtSGM@L6U zNJvUbN=!^lPft%%Q&Ut_R9aeETwGjUUS3~cUu0xtWo2b&XJ=?=XlZF_Yinz4Y;0|9 zZEkLEZ*OmLadC2Ta&vQYbaZreb#-=jc6WDoczAevdU}0*eSUs^e}8{~fPjL6f`o*G zhK7cRhlhxWh>3}bjEszpjg5|uj*pLzkdTm)l9H2?la`j2nVFfInwp!No1LAVpP!$g zprE0lp`)Xtq@<*!rKP8*r>Uu_tE;Q5tgNlAt*)-FudlDLu&}YQv9hwVw6wIfwY9dk zwzs#pxVX5vxw*Q!y1To(yu7@)z;S5 z*VotC+1c9K+TGpV-{0Th;Na!u<>uz*=jZ2o4wQ)i000eiQchC=fEKKD8IfKqBJP%EXNC?X20K9$$o+U(W3u^QVAPXFxOl9nWi0)X&? z&1*ZdOo+0(D|K+V%&Q#9gvg2#YUVKPu?!)EoF{+s8W>&U2WRI>pcnZVO?_SuGmP+G zCzJ|*L@7kkg41$bDS*Y@Q9MlGRE$2t}mS&_C7qS&y2mPs_N$O(B0GPD`VQri#o>Ps+5+?F_zP` z(?LGTO!k8@#=aih`MIa0_s=g)Stxi1bRte8^Iq+`2OY}00000 M07*qoM6N<$f}e<{V*mgE diff --git a/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-left.png b/3.1/themes/greydragon/css/colorpacks/greydragon/images/view-left.png deleted file mode 100644 index b5b93c7a5e0e191565a8970a8bbae7cbcbddcca5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 393 zcmeAS@N?(olHy`uVBq!ia0vp^fNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-sFbFdq&tH)O6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q7ALkGu1P(>pfEj zRCLDE#W6(VeCq_myh9EGuJ?PE7jTEJ^6;2u!5FfyDc`}>`MLKG20M2vy@QG?E-h<1 zx8tK9*X-2ItUsSlewO)fdTr!{l&XNw=37NCO!;-S=kQX=cJ5X`1*I7pYFv)8AuQ}N z3NxBJirCIE{8PF3Sn9yXD~Ej+vF|>sQkA#OM$Gnw(<1dr)7!OPhn`$ELF7lu)#qBO z-}C!!UcGqj!t|6Q44xmT3$r1`;yDe%%u3)VqWFH*vAvT iTAj%IJ+W#2YyEKX@E@h|rItW{F?hQAxvXNn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N!uqB*-tAfuU^jSqmVK zv%n*=n1O-sFbFdq&tH)O6qGD+jVKAuPb(=;EJ|evNX*PD(erZ+Q7ALkGu1P(>pfEj zRCLzU#W6(VeD4H9zC#8)uKVxu#J)Vz(h|}seBg?9L9<`%jU6oP4`gy;nw$+b8ckhW zlIirwL}jf+!KbA!PX6oj-EHG6bXZz+S)k_Zjqi8Q)_BfsB*);Yqom}pnemP2MuF7% z;T03lHW#Y=s5oTxOCo%=004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00009 za7bBm000id000id0mpBsWB>pF8FWQhbW?9;ba!ELWdKlNX>N2bPDNB8b~7$DE;K%k z%ys|(1C>ccK~zXfwU+%)QehOw`2+f~KSVb-r=m`sI;$TdH&HYxr!|ej88Opf&Ri*T z8s=K+Y^)`<@+DI%sL+%$3umIesF{^skOX^Kic0#8=V_P8%T=6px3lN&-t&CUIrsUV z1H9PSSg;ls7l++gOiYZ2#UoJOKfWk4Sa8a|eQ0QCKu%81wuw+CpU>xXOioTtI3t(A z(E9{dixqZ9v<%XkcI0ydr^2qfs(vx$i-DSz!%|S zSv|~SpRxa7CK6LbXt~(|o6QCyv(3VyOkXf@V9PAXmZ4Ol!kOF($P3EQP zYzWfwz|1?~n=RST2`()?N6T!w+zg|B5X{s(9ADkv)D5-G+fi5~hxYbeOiX>G4dvSg zoZ$HQct1rjv$Gg8n=v@}0#Ez7!3hot3CVc*atPz&W4P0L8zm*h;73Q% z<}jH?u&}TIgF(-!Wlk_D&3zzy19f_6Xy_h=LWzuY;nv__TY4ws#4{_%4N3oB7&|F> zSy@>iCXq-WlgV~%ERTYBQf4r|lu5D-#yMLoMq67OM^*XpmDrbOzU}1VX`UZ?x{Px`gVu732m6c`Gsnr4D0l_!4x4eL^pBs4l;yv0m zomgE4o(`JebVp!=OiWBbAP{&32nZI5GU=va<0!6FUPIYQ71Sk-kRC3^$g3IX`d;C5 zX*v1_hCJ}@?(Xfu@d*iZPMC~lRLIn*Ije!{bTf_$q-d(Y1;+9V;#?_2*;42p4Z!7c zAvHC1doWQ5`BnJoScbBw618R5P*r>tDrG&|wD+;$0`iU*A+O*poNIt@wq!r2Dr##l z(K0)3cH-UeBvuyPwBPRU>Rw!H8>qOTg5F?+eenm<($f46kP|Ev3jGw}Tv~#KVK6y4 zh4D9U;9Pa$e8u_TRY6KgOiaY|^aoh27WDQRP*YP4!J&h+Ic8?QV10cZqocz+(=h>u zg@w@rq5ltjuB4lB0l0000< KMNUMnLSTaZwCpbc diff --git a/3.1/themes/greydragon/css/colorpacks/wind/colors.css b/3.1/themes/greydragon/css/colorpacks/wind/colors.css deleted file mode 100644 index 09ab5a6a..00000000 --- a/3.1/themes/greydragon/css/colorpacks/wind/colors.css +++ /dev/null @@ -1,184 +0,0 @@ -/** - * 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/3.1/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif b/3.1/themes/greydragon/css/colorpacks/wind/images/ajax-loading.gif deleted file mode 100644 index 53dd589fa194f5db985e4301c7a73ed4f1b9ad99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2545 zcma*pX;2hr8VB%~zPqQp=@B)`y1PS96NXtx1_oS2g;AJ+5se@Q;|&UOs2qwM!p4Ca zhJiUal?ExBdstaT~?f3m?cZgNh{frmzMrfcJ8)3 z;P)BJevgQNtw(cfF&90SnBnnr_M&xm@O%6 zzG;!w8(-4o!w_SKEsx_t8i2dHA{$xug+5j_t}~5!rEMsQGpY^uf8#e!ez!6*c<$`# z%2he_t-h<5RT`BLN|GpKWC}6HY^k+5Dom~L=dCyXf!71bXd=Do;)LbM zv+gGZ*&l+=TYuPh^HJ+{Um3v9W8Dqi zZsIDi!j*rKg;1w`oCOc;={#&)!ZF-E-<0u^i2Q3W!xltD%v-T4#;x)CfF(1Ouv)`p z>uUosDu9aCbKX*^H)pSF(Clw+wK}`3LC8Vd5wn~@j##n&8u(Or9|$_$-*q^|8tR^Ze!v`8XRU12fiFxN&Z^HeB_6{>ZFruBWn5U6U_WQI--B zG~o}Ys+mlEb+PC3o7FVVvN+9%m7%O}Y_c3WoYls|wT3|PzzV{wM+0F|$H`%9CT0Y^ za*mZIUl3}$c$+|-?~lU1Lc0B}sn(uvcimZ5#)MR#za@NWsrn^X=yb^$k3_>|w9gkR z>#sW<_Ea0KR$g0dg}fy7K69z6?%wR47d!o@ zGKEctobW(W=VY|Az1{V$NH$hYU5!_r3z~t<1_3#$IhT`+-0m8Lu3WANjbGvNg1vn1 z=E&h$lM^*FtCrg64J+q9{~$Yc&oK@)FJ9|wdu`CBEMKgD->DcQRuxrUW36azX6kqE z(WAIz&9a^4uqfQXZ?4*+k}M^TI=80q9GA}G8+>bb(fyeR`Q-S93>Q-=OX7E#U^f4~jWk3u>uE&r}55)J9D8+*U}{&~xy z+xheT3lqZ$_0RMat&nbp<_ki!f?+E=jik2kBu@#UnX z0AcCVU-1x`*#iFAGPG4?H)8U+YtUJCS?At3FP6 zWjMo76`C~5oga!z%d+xx$x-ljIYP1$9YjNGd0H&jsWL7XsM^)8O;wB>PVnYYXxtSh|$ifidD)RGQ}R zwHJrNIdVjTqdEklY&;>>dZRxDK`F>#B8Ki@pp8f7rwM2mppszvcw{I`Q1%>g09l6Ekr2-S;;<|g2awum zsj!Q?)Pbyi5x+M!F@WFk6jsdhHiry`x0GIzjy;Yj%rtk*Y|Rg9EEqf3wBpTM<@>(0 zk0}s;bd@I0{9$O~|I{qUIC4vLX&EGrCS`%OyAe_n3}9mSiO7}(QE&#Jdx176W`mkS qJBjK|(6Aas*VCO0)PD%QJkkj;=v4CXc=>?~L(zU6eu&)uW=Q7Xo| zWA@_khq6m{?A&S5q0ljHmVJDXY*3-+{iizZ%>4OGF?VN4?l$6ilYV{ip{4eW-1%{9 zO{?~1yo%H3`FQqaZ~0E+&MR(NFD2)0(BAi9lW5tI?4t@LFH{yC^V06BatS`-c;k4x z{FSJ!JUb`XKRnT>-|4e-lIDjWwbO$+1*^G^tA%OxMsf5QL( diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/ico-error.png b/3.1/themes/greydragon/css/colorpacks/wind/images/ico-error.png deleted file mode 100644 index c37bd062e60c3b38fc82e4d1f236a8ac2fae9d8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 701 zcmV;u0z&N#0$9Ug7g~-`rQ^qx~m@y2OU8A z#zh~=7n#Z$Z*fx-GOtDf07cgx0suCz_W(2~Y(0tf@FX@P6EPuM_dgn$vj9LucO)%W zw%HgMW>=#oL>nZ>M&NEf08>)#)k<{$fCT_r>rPi=BV=hFh6WS^qqze>C6Ek}o{M5% za|@JGowu0t{&hgNzySHZxy@LTNh);YzZ2zSp_ zl$^T&Dnc|NLb&RD_!4>pt@VHdP)ZGER%5ZmWEe$lryR&y;2u^3cOkO4#6c%-(EY6a{600000NkvXXu0mjfxS2AI diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/ico-separator.png b/3.1/themes/greydragon/css/colorpacks/wind/images/ico-separator.png deleted file mode 100644 index 3e158515556616fcf3cab5e837664263f6c58c59..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2VkYHF5IUx~9v7|ftIx;Y9?C1WI$O_~$l?3?( zGcc4*K5GHwNtC!olmzFem6RtIr7{F0X6BXX`MHKDlo{(8o2`8QNEN6?(bL5-gyVX0 z!U4v#yhv;IWAnD9iq5gkd_O1j#iA2gADI~V9C;r3-B_CiRLtP%>gTe~DWM4fa9k}Q diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/ico-success.png b/3.1/themes/greydragon/css/colorpacks/wind/images/ico-success.png deleted file mode 100644 index a9925a06ab02db30c1e7ead9c701c15bc63145cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 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 Pk4-YvHMw4)?HKcaF3|lA diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/view-calendar.png b/3.1/themes/greydragon/css/colorpacks/wind/images/view-calendar.png deleted file mode 100644 index 13e0e8fa6ad01636e87a376aebada4612d937186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 637 zcmV-@0)qXCP)D7ei;IhljEs$qjgF3vmX?;6mzS8Bn3dCU$;rvf%gfHr&d<-!(b3V;($dq@)6~?|*4Eb7*VowC*xA|H+S=OO+}z^g z;^X7vlt)=I7_<>+9?6?CkCB?e6aG^Yioc^z_3;Z)X4i010qNS#tmY4#5Bb z4#5Gqk!$S$000?uMObuGZ)S9NVRB^vP+@6qbS_RsR3LUUE;TMRK8?(F0002gNklXRGqXq|E9aC%$SE>sC?yodM2ylL?*BGC|I-$i?Rt0TTOKCqib?KW zAaB90l*C7nq)MMQ@)~3h^ULGCrj5J;?dq0`!%dv4(?)hc4JqL+D@Vn|eU3Kr63o28 zPei?})dmV}A;b!Iw2?RPt^X|6*2dg^Iu32*oeSh65B?wfC&wND X`katw^)ehH00000NkvXXu0mjfcAGp5 diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/view-comments.png b/3.1/themes/greydragon/css/colorpacks/wind/images/view-comments.png deleted file mode 100644 index f33bdf1952832e6e119e24fcf3589d040bdef66b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4NyWWz$e7DaN_pUr%zwL zeEG_iD|hbPdGzSflP6D}K7IQ9`SaJWU%!3(_T9U8pFe;8^5si$(vdQtF3tjv$YKTt zzC$3)D5~Mr02Gugag8Vm&QB{TPb^Ah2uRG#E79|F4N)jF)-%;JvFkkp(pl!|;us=v zIXS_BX-|3L119G0@9yqybaKdIW`2H#lSjc(;Os@lz|w__8yj^W>M=`fQZ?AX=CE}W zn}LC*!_k={5)!Nn9`!KsoH@2fykXHE@g81@h!~jzY>b&~-#&d}n(+6HLszyai@}Z+F-V^$eg37(8A5T-G@yGywqUHG%>F diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/view-full.png b/3.1/themes/greydragon/css/colorpacks/wind/images/view-full.png deleted file mode 100644 index e465d36695fad7ad03029fbd8e1b54ea4b2949e5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 537 zcmV+!0_OdRP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0001) zP)t-sJ3Bi)Jv~K5MMg$OO-)TsPEJoxPf$=$QBhG+Qc_h_RaRD3S65e9SXfzESz20J zTU%RPTwGmUU44Che}8{*k++A3hlq%XkB^U#kdTp)k&=>SZpP$Ri%hS`-)YR16+}z^g;^gGy<>lq)=jZF|>+S9B?(XjM^Yird z^n|kOv;Y7A32;bRa{vGi!2kdb!2!6DYwZ9402y>eSaefwW^{L9a%BKeVQFr3E>1;M zAa*k@H7+zhjm&lc006~FL_t(2&yA4B4#F@L1Vatg^xi@M8yh$H|Bqv}J_Urtljbsq zk=|yD&&w9C>cPagJ{EDkoRJ9Q{pNJ``_-Iy80WS{>2|9*aWIeS!5FhYUkw_>z$kSn zZwd{fV5EeRXb=f=uO5un+w!8(AR5M67>fq6Fd@JMG)RDXRuAU;_y5*t_6D(8&q!LM z)gSs!qejvi`9Qe5Q6sX^mWrB^u<{8f`JsXppo9j75W_HDCf7 b00000NkvXXu0mjf1~lf( diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png b/3.1/themes/greydragon/css/colorpacks/wind/images/view-fullsize.png deleted file mode 100644 index 58b3e0b471b8503f066676fa3242893124462559..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 365 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!3-qjE#*>x6id3JuOkD)#(wTUiL5}rLb6AY zF9SoB8UsT^3j@P1pisjL28L1t28LG&3=CE?7#PG0=Ijcz0ZP~e_=LC?PTW3y`R>!F zPoFt+=JMssSFT*SbLY z*ARs=V?9$n6T99sWk5w`o-U3d8t30$ILLQMLBKW8O=nvWx09AbaXS(L4GX%26Zrqa`1y-zm94<6=Bc<&Vx_9W1EuU}h@sqKETpNo>tU!1^R zdM9`C93f@PW4aTI@6EseQtA4s##a*Oz2f{@^8ea@+unbdA>@JJ+=dsLpMWl4@O1Ta JS?83{1OQ!uo!0;W diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/view-info.png b/3.1/themes/greydragon/css/colorpacks/wind/images/view-info.png deleted file mode 100644 index 2cc7a68ea0d6970ddc06d7bcfb26ab9d42ef8de7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8g%!VDyDo&1~%q*&4&eH|GXHuiJ>Nn{1`6_P!I zd>I(3)EF2VS{N990fib~Fff!FFfhDIU|_JC!N4G1FlSew4N$@$z$e7DxMN}A#O>>L zpE`Z|^ySN!uUxru=gyr+j~+dF^5p5$r_Y~1fBpLP+qZAuy?gih^XD&Lz8Lv%@B{U7 z7I;J!GcfQS0Aa?gYn_}xLCF%=h?3y^w370~qEv=}#LT=BJwMkFg)(D3Q#}*A-ZN!D zMN>Ro977~7Up=>x^N@o`!^8jcuCw_poVq||ZmaK{BaAHJMU!}v9CY3kE!q0fHOa-G zJY98H(|wltohT>s1K Zn3G!?+Ef3|mH@hs!PC{xWt~$(698=Do3Q`@ diff --git a/3.1/themes/greydragon/css/colorpacks/wind/images/view-left.png b/3.1/themes/greydragon/css/colorpacks/wind/images/view-left.png deleted file mode 100644 index b51e336884659567c903a75ee1d435cdebef3eba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 539 zcmV+$0_6RPP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0001) zP)t-sJ3Bi)Jv~K5MMg$OO-)TsPEJoxPf$=$QBhG+Qc_h_RaRD3S65e9SXfzESz20J zTU%RPTwGmUU44Che}8{*k++A3hlq%XkB^U#kdTp)k&=>SZpP$Ri%hS`-)YR16+}z^g;^gGy<>lq)=jZF|>+S9B?(XjM^Yird z^n|kOv;Y7A32;bRa{vGi!2kdb!2!6DYwZ9402y>eSaefwW^{L9a%BKeVQFr3E>1;M zAa*k@H7+zhjm&lc0075HL_t(2&yA3=4udcZM9)dYR*40=lmUsQo%;XZ!PJiJz|?+0 zoSs931gH{;-g0&>_DO@y3Fy|sD?l+-uAVe@2H>D7A{q+HR|!;70y5^KaIDop2Dt~2 z^wD@W%qK>^ilGoo4^}?>4auJ)-WIMS@878c=nh8p9-@NzeHQ$B)i=m zj-@eHZWvYX#=kV&E)5Z<1Ctn*G^nJ9>dlzb;9i?FcsFTKaXm-rmo#jdr|Q)>mWDK! d^004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0001) zP)t-sJ3Bi)Jv~K5MMg$OO-)TsPEJoxPf$=$QBhG+Qc_h_RaRD3S65e9SXfzESz20J zTU%RPTwGmUU44Che}8{*k++A3hlq%XkB^U#kdTp)k&=>SZpP$Ri%hS`-)YR16+}z^g;^gGy<>lq)=jZF|>+S9B?(XjM^Yird z^n|kOv;Y7A32;bRa{vGi!2kdb!2!6DYwZ9402y>eSaefwW^{L9a%BKeVQFr3E>1;M zAa*k@H7+zhjm&lc007NNL_t(2&yA41P6I&@gFUmS>mt!4X-I*TCRBO<=b(ZT3MzO2 zv|jAVk4RFGJAK-d<@L=tmbiK*Y%YcO3B)J+rkL{qNRU%R5rVok^kNt(!#RYkG${|t z^Tob4fIW%cl~K&1OBv&o^Hb*}ZJ{v?F`9D%-|Q=+zlL*KQ$`_MYw|SD(+mpdtn_Kt zwOHKjp17}#7jj?Bx&5z=Jhve$O++5scqN~Wl&dy$Uv2!&8FhGzCyimGa%a5M jeQm_@@p7+^+ctgysS004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00024 zP)t-sTy~Ucft_!Ko^XeubBLgEh@p3krFo90ev+zxldEx&w}Oj$IH;q)Y;P0)6~?|($(75+1uLN+uYpT*xcaW-{9Qe+9|9?e6aG^Yioc^z^e>p~e6J010qNS#tmY4#5Bb4#5Gqk!$S$000?u zMObuGZ)S9NVRB^vP+@6qbS_RsR3LUUE;TMRK8?(F0001_NklG_HmA)_#vW8YlP0EEpSO{4`ODPxgZm}9#jVz(p z1~qIng8{$I33_kYEhE2%pYRQxq}cc+;tl=Ca2oN1e&qoDrNMu(KPmPD4?kHU47is& P00000NkvXXu0mjf6khul diff --git a/3.1/themes/greydragon/css/forms.css b/3.1/themes/greydragon/css/forms.css deleted file mode 100644 index ab246779..00000000 --- a/3.1/themes/greydragon/css/forms.css +++ /dev/null @@ -1,107 +0,0 @@ -/** - * 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/3.1/themes/greydragon/css/layout.css b/3.1/themes/greydragon/css/layout.css deleted file mode 100644 index db55e4af..00000000 --- a/3.1/themes/greydragon/css/layout.css +++ /dev/null @@ -1,38 +0,0 @@ -/** - * 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/3.1/themes/greydragon/css/menus.css b/3.1/themes/greydragon/css/menus.css deleted file mode 100644 index 3ba173f2..00000000 --- a/3.1/themes/greydragon/css/menus.css +++ /dev/null @@ -1,56 +0,0 @@ -/** - * 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/3.1/themes/greydragon/css/modules.css b/3.1/themes/greydragon/css/modules.css deleted file mode 100644 index 371434f8..00000000 --- a/3.1/themes/greydragon/css/modules.css +++ /dev/null @@ -1,135 +0,0 @@ -/** - * 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/3.1/themes/greydragon/css/old_ie.css b/3.1/themes/greydragon/css/old_ie.css deleted file mode 100644 index 9a5da7b8..00000000 --- a/3.1/themes/greydragon/css/old_ie.css +++ /dev/null @@ -1,16 +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/3.1/themes/greydragon/css/screen.css b/3.1/themes/greydragon/css/screen.css deleted file mode 100644 index 17ecf082..00000000 --- a/3.1/themes/greydragon/css/screen.css +++ /dev/null @@ -1,224 +0,0 @@ -/** - * 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/3.1/themes/greydragon/helpers/exif_event.php b/3.1/themes/greydragon/helpers/exif_event.php deleted file mode 100644 index 27b617a6..00000000 --- a/3.1/themes/greydragon/helpers/exif_event.php +++ /dev/null @@ -1,43 +0,0 @@ -is_album()) { - exif::extract($item); - } - } - - static function item_deleted($item) { - db::build() - ->delete("exif_records") - ->where("item_id", "=", $item->id) - ->execute(); - } - - static function photo_menu($menu, $theme) { - $item = $theme->item(); - $menu->append( - Menu::factory("link") - ->id("exifdata-link") - ->label(t("Photo Details")) - ->url(url::site("exif/show/$item->id")) - ->css_id("g-exifdata-link")); - } -} diff --git a/3.1/themes/greydragon/helpers/greydragon_event.php b/3.1/themes/greydragon/helpers/greydragon_event.php deleted file mode 100644 index 6f84512d..00000000 --- a/3.1/themes/greydragon/helpers/greydragon_event.php +++ /dev/null @@ -1,41 +0,0 @@ -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/3.1/themes/greydragon/helpers/greydragon_installer.php b/3.1/themes/greydragon/helpers/greydragon_installer.php deleted file mode 100644 index 461e6914..00000000 --- a/3.1/themes/greydragon/helpers/greydragon_installer.php +++ /dev/null @@ -1,30 +0,0 @@ - \ No newline at end of file diff --git a/3.1/themes/greydragon/helpers/greydragon_theme.php b/3.1/themes/greydragon/helpers/greydragon_theme.php deleted file mode 100644 index 988da98c..00000000 --- a/3.1/themes/greydragon/helpers/greydragon_theme.php +++ /dev/null @@ -1,30 +0,0 @@ -' - . $theme_info->name . ' ' . $theme_info->version . ''; - } -} - diff --git a/3.1/themes/greydragon/images/avatar.jpg b/3.1/themes/greydragon/images/avatar.jpg deleted file mode 100644 index 71166cc42e6ea4a50267bc949ee3bb7efc820c5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1442 zcmex=kx|JhscGpMnOVgprDf$6 zl~v6xt!?ccon4bAPnkMx`iz;g7A;<~blLJ1D_3pWyk+aQ?K^hvI&}ER(PPI?oIG{u z@|COCuHU$M>*1rvPo6$|{^I4UkDoq&`TFhqkDtFl{$gZch6fo|e+dHp#l*tG%)$=x z7b88f2KE_o z9%~}YXK;@p{B?_ghnW!=dCYA@qsIqS2QR&ml z6IRM(ym@!LVZ|!R8IvzCcz#(o>)MIETX(;eH{H9t#QrEt`mgJ||L}jfQeJg!<;wFi zdAn*SIZFw6bLQQ*zLG4j<=XQkVV;HM0{;2WK7@DbzS+M;{bDJ{T63c<{rO6}rb?zw zV*dT(0{_oU6>rHm$9Dc_*d%zRt~Y*pyJzS0%qAPT9S;1ql`_xoU)vY`a&+g~P&Y3AFzAtxM;pLy}X@VF{l<;ey7GgY=5F0A({v%SIhCotA-VbbZ; zg5T^XC6WqnM*6SM(qP_m>C4|=aWj6`{hOO~Kla*1nSWc~&1YHKb^6G5tI{L$LaNrz z^a$LbJn0?VeW^MB86*Jj^^FQSz)rF`YR6+?uMY@Nam%=G87um6$# zu>WY(vX#s39$)Ui{I+vxe(AT<6F$^T^q3?pl;ExQm4$cpmgbX38EWNjMJT^x>~}n8 zyKn#Xr8ToZu8YY3e($`juy?;fOy1*Z zw|gGX`}6g0<@&BY$saBsmC9SaGWP89JMp*9mfIfKyDe;v#FMFWeO56}GHPu4^MH?m zrO`p&Bs;3^ta@PW{-SebFaKUq{}mW}yz|Sqc86%VAK4GdN+0;;`n|Iz{=sc|0n@d;o1?RK{hP@8bcxul+$o8?d%kix&;2cOs7v{U)P=Xm1QJ=KfR%`aTq?af%!U=#d!dgni$*Z&!K z|3sL~m#onHenjx4cu}Uo%U$257S diff --git a/3.1/themes/greydragon/images/blue-grad.png b/3.1/themes/greydragon/images/blue-grad.png deleted file mode 100644 index 36e0f6bc25b10ca27ca968108563518fe5dc49db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^tUxT!!2~21I85*dQj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>Jikv)M978H@CDnX=Db6gEkmSIs^Z()xp8pr0{(pS^|NIG+ z|398e{=m6H{efN6yPy6J_J`|F-ErUaKah395q{5i%p3*`AMWWWCU;$$4>XOz)78&q Iol`;+0KbGg>;M1& diff --git a/3.1/themes/greydragon/images/button-grad-active-vs.png b/3.1/themes/greydragon/images/button-grad-active-vs.png deleted file mode 100644 index dc641725f90ae1c2600aa73f61fd71852de7b2ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^tUxTs!2~3;Wt?9DDajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_d9MOL0Jjv*Ddl1k3aJ<`B3Ln45YTUl8-&W@Q`+4B|WrDSr z1<%~X^wgl##FWaylc_d9MYf(Ujv*Ddl6qdpoNr)>`1k*R|C#h82i6y#Bx=~&{;RB1 zKk>gk{-1o?zxohU`MZ1SMP`4WKkZ?sTs#9K8^fQ=ep~#F0-b>dF?hQAxvXra@ocD)4hB}-f* zN`mv#O3D+9QW+dm@{>{(JaZG%Q-e|yQz{EjrrH1%H3s;Exc(n#urXyR3(zrIB|(0{ z49sktf^te)y2h3+9-&dOX+`CY9TO%`o4IJ|s@2-2W!cm@VW zW=|K#5RLOkC&fk|R^Vwhb~Q`M=)LRmTH^50{y+caGnjIhhueL2I`7KQUXgi%`M`e7 zo%ajh)ZZv54t!f?n08rl*0IPVer=m)UkokZ6I^h(U-!1t$_c@G0iGc`5vMwh8Yai6 zgzLtO-1%^O*HgvQoh!rp7X4A;Z*c9C)|ujW?JJ6=mowip5roe zIk#Sa&7o&?8q3$k%Xey499y;1*#FG$)0!f4zXw`G_fJr&yNn{1`T?2eVT!WkTr!?&^Y1$v$bU3)_cyQn8;92KVmR%~@c&)W>f9tIM(?IBO z>)vbAHeOn`?C|!D$G7i2J?-SRWhbv~KY4EZ$xGW$UR$uOAQEUGXMsm#F#`kNVGw3K zp1&dmC@5Lt8c`CQpH@L&ZCWNx zGxted(ER#$isbZTELLr{Gh`;dx$w>LzPG$h(aYj*XN$J`r>;4Zvw>6Ql$Cw$U%L}u p_ob@+p0?B>HSfmBFURXYFiNK=n!R29=seKn44$rjF6*2UngBBZs^b6v diff --git a/3.1/themes/greydragon/images/favicon.ico b/3.1/themes/greydragon/images/favicon.ico deleted file mode 100644 index 66531d8e01618d1fc7b58f386607ea67619c7da8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmcJL?@Lor7{`wo1og(SdeMvQT|v+v(2L&px|aq)f(e0<^`@UnQNAc4!^+=NXriOU z6vEY@EoYkIM!6qucW!syd(Zvh)$Pu$xe~vfdu3ruqKMArd!F+==YGD=Ifsxk_El66 zHXk8%y9hZ*2sy^YO02R~XTchkdkHc1zlM-ZoLkYCFK04q`~S8_DwRH=BqFO?lEV_8 zm{KUcV;IJsKk>=u^CvYSt_!XnL`EJVD~7Qgo`NdGBQm8mzw;;*3WrsJ*Xg7KzLsiC zKWnh~xt6n7a&|!D{E$UqTve2#r9SL@`=mrHq_Ji8@@Vx9xg*ItNj&d{G+2T4! zW7pFQ9*E91h^}tSN1q#I=V2a!uWnbKHDDK}HWcbn#$F1~0sJq+_s z=DD=#A>_5>SSeGQr#aslBRT~mHXF){(F0i_QpMhRG`keYtgotvO8?&}&&i8*-?Hh` z+xM~(cYJ&PuWO*40EDEO_(nq9RfII{X0GL1&P^=1%h(>%I;(9kZC)T2V{+dCIgajE diff --git a/3.1/themes/greydragon/images/missing-img.png b/3.1/themes/greydragon/images/missing-img.png deleted file mode 100644 index 12b7394fcd832ee62201853240fb4a15a39bd793..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33136 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 diff --git a/3.1/themes/greydragon/js/ui.support.js b/3.1/themes/greydragon/js/ui.support.js deleted file mode 100644 index 353a30cc..00000000 --- a/3.1/themes/greydragon/js/ui.support.js +++ /dev/null @@ -1,156 +0,0 @@ -/* -* Grey Dragon Theme: JS support -*/ - -jQuery.fn.extend({ - myAjaxLoginSubmit: function() { - - var myAjaxLoginSubmitOps = { - dataType: 'json', - success: function(data) { - if (data.result == 'error') { - $('#g-login').html(data.form); - $().myAjaxLoginSubmit(); - } else { - Shadowbox.close(); - window.location.reload(); - } - } - }; - - $('form#g-login-form').one('submit', function() { - $(this).ajaxSubmit(myAjaxLoginSubmitOps); - return false; - }); - }, - - myAjaxSubmit: function() { - - var myAjaxSubmitOps = { - dataType: 'json', - success: function(data) { - if (data.result == 'error') { - $('#sb-content form').html(data.form); - $().myAjaxSubmit(); - } else { - Shadowbox.close(); - window.location.reload(); - } - } - }; - - $('form').one('submit', function() { - $(this).ajaxSubmit(myAjaxSubmitOps); - return false; - }); - }, - -/* - _ajaxify_dialog: function() { - var self = this; - $("#g-dialog form").ajaxForm({ - dataType: "json", - beforeSubmit: function(formData, form, options) { - form.find(":submit") - .addClass("ui-state-disabled") - .attr("disabled", "disabled"); - return true; - }, - success: function(data) { - if (data.form) { - var formData = unescape(data.form); - $("#g-dialog form").replaceWith(formData); - $("#g-dialog form :submit").removeClass("ui-state-disabled") - .attr("disabled", null); - self._ajaxify_dialog(); - self.form_loaded(null, $("#g-dialog form")); - if (typeof data.reset == 'function') { - eval(data.reset + '()'); - } - } - if (data.result == "success") { - if (data.location) { - window.location = data.location; - } else { - window.location.reload(); - } - } - } - }); -*/ - theme_ready: function() { - try { - Shadowbox.setup("a.g-fullsize-link", {player: 'img'}); - Shadowbox.setup("a.g-sb-preview", {player: 'img', gallery: "preview", animate: false, continuous: true, counterType: "skip", animSequence: "wh", slideshowDelay: 5 }); - - Shadowbox.setup(".g-dialog-link", {player: 'ajax', width: 500, height: 420, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup("a#g-login-link", {player: 'ajax', width: 340, height: 190, enableKeys: false, animate: false, onFinish: $().myAjaxLoginSubmit}); - Shadowbox.setup("a#g-exifdata-link", {player: 'ajax', width: 600, height: 420, animate: false}); - Shadowbox.setup("a#g-disclaimer", {player: 'ajax', width: 600, height: 420}); - - Shadowbox.setup("#g-site-menu .ui-icon-pencil", {player: 'ajax', width: 500, height: 420, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-pencil", {player: 'ajax', width: 500, height: 420, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu .ui-icon-plus", {player: 'ajax', width: 500, height: 390, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-plus", {player: 'ajax', width: 500, height: 390, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu .ui-icon-note", {player: 'ajax', width: 500, height: 370, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-note", {player: 'ajax', width: 500, height: 370, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu .ui-icon-key", {player: 'ajax', width: 700, height: 300, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-key", {player: 'ajax', width: 700, height: 300, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-site-menu #g-menu-organize-link", {player: 'ajax', width: 710, height: 460, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu #g-menu-organize-link",{player: 'ajax', width: 710, height: 460, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup(".g-context-menu .ui-icon-folder-open", {player: 'ajax', width: 400, height: 380, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup("#g-site-menu .g-quick-delete", {player: 'ajax', width: 400, height: 150, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - Shadowbox.setup(".g-context-menu .ui-icon-trash", {player: 'ajax', width: 400, height: 150, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#g-user-profile .g-dialog-link", {player: 'ajax', width: 500, height: 280, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - - Shadowbox.setup("#add_to_basket .g-dialog-link", {player: 'ajax', width: 500, height: 360, enableKeys: false, animate: false, onFinish: $().myAjaxSubmit}); - } catch (e) { } - - try { - $(".g-message-block").fadeOut(10000); - $(".g-context-menu .g-ajax-link").gallery_ajax(); - } catch (e) { } - - $("#g-site-menu>ul>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/3.1/themes/greydragon/libraries/MY_Theme_View.php b/3.1/themes/greydragon/libraries/MY_Theme_View.php deleted file mode 100644 index 4f579ec2..00000000 --- a/3.1/themes/greydragon/libraries/MY_Theme_View.php +++ /dev/null @@ -1,313 +0,0 @@ -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/3.1/themes/greydragon/theme.info b/3.1/themes/greydragon/theme.info deleted file mode 100644 index cea1d8d0..00000000 --- a/3.1/themes/greydragon/theme.info +++ /dev/null @@ -1,6 +0,0 @@ -name = "Grey Dragon Theme" -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/3.1/themes/greydragon/thumbnail.png b/3.1/themes/greydragon/thumbnail.png deleted file mode 100644 index 4b80ecaf35a50ced69c426d9c6b9e16b8af6a322..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25791 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 O0000 -
    - 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/3.1/themes/greydragon/views/block.html.php b/3.1/themes/greydragon/views/block.html.php deleted file mode 100644 index af29546f..00000000 --- a/3.1/themes/greydragon/views/block.html.php +++ /dev/null @@ -1,33 +0,0 @@ - - - - -
    - is_blockheader_visible): ?> -

    - -
    - -
    -
    diff --git a/3.1/themes/greydragon/views/dynamic.html.php b/3.1/themes/greydragon/views/dynamic.html.php deleted file mode 100644 index 1f787cf3..00000000 --- a/3.1/themes/greydragon/views/dynamic.html.php +++ /dev/null @@ -1,39 +0,0 @@ - -
    -
    - dynamic_top() ?> -
    -

    -
    - -add_paginator("top"); ?> - -
      - $child): ?> - get_thumb_element($child) ?> - -
    -dynamic_bottom() ?> - -add_paginator("bottom"); ?> diff --git a/3.1/themes/greydragon/views/exif_sidebar.html.php b/3.1/themes/greydragon/views/exif_sidebar.html.php deleted file mode 100644 index 115bfc86..00000000 --- a/3.1/themes/greydragon/views/exif_sidebar.html.php +++ /dev/null @@ -1 +0,0 @@ - diff --git a/3.1/themes/greydragon/views/info_block.html.php b/3.1/themes/greydragon/views/info_block.html.php deleted file mode 100644 index d3860584..00000000 --- a/3.1/themes/greydragon/views/info_block.html.php +++ /dev/null @@ -1,24 +0,0 @@ - -
      - owner): ?> -
    • - - owner->url): ?> - owner->display_name()) ?> - - owner->display_name()) ?> - -
    • - - captured): ?> -
    • - - captured)?> -
    • - - description): ?> -
    • - bb2html(html::purify($item->description), 1) ?> -
    • - -
    diff --git a/3.1/themes/greydragon/views/login_ajax.html.php b/3.1/themes/greydragon/views/login_ajax.html.php deleted file mode 100644 index 76028c4e..00000000 --- a/3.1/themes/greydragon/views/login_ajax.html.php +++ /dev/null @@ -1,41 +0,0 @@ - - - -
    - - - - -
    - diff --git a/3.1/themes/greydragon/views/movie.html.php b/3.1/themes/greydragon/views/movie.html.php deleted file mode 100644 index ec870608..00000000 --- a/3.1/themes/greydragon/views/movie.html.php +++ /dev/null @@ -1,43 +0,0 @@ - -
    - 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/3.1/themes/greydragon/views/no_sidebar.html.php b/3.1/themes/greydragon/views/no_sidebar.html.php deleted file mode 100644 index dd61bb77..00000000 --- a/3.1/themes/greydragon/views/no_sidebar.html.php +++ /dev/null @@ -1,24 +0,0 @@ - -  ?> - diff --git a/3.1/themes/greydragon/views/page.html.php b/3.1/themes/greydragon/views/page.html.php deleted file mode 100644 index 13664d65..00000000 --- a/3.1/themes/greydragon/views/page.html.php +++ /dev/null @@ -1,147 +0,0 @@ - - -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; -?> - - -"; ?> - - - -item()): ?> -item()->is_album()): ?> - $theme->bb2html($theme->item()->title, 2))) ?> -item()->is_photo()): ?> - $theme->bb2html($theme->item()->title, 2))) ?> - - $theme->bb2html($theme->item()->title, 2))) ?> - -tag()): ?> - $theme->bb2html($theme->tag()->name, 2))) ?> - - - - -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" /> - - - - - - - -page_top() ?> -
    - header_top() ?> - - - - - - -guest) or ($theme->show_guest_menu)): ?> -
    "> - site_menu() ?> -
    - - messages() ?> -header_bottom() ?> - -loginmenu_position == "header"): ?> - user_menu() ?> - - -show_breadcrumbs): ?> - breadcrumb_menu($theme, $parents); ?> - -
    -
    -
    - sidebar_menu($url) ?> -
    "> - - album_menu() ?> - - photo_menu() ?> - - movie_menu() ?> - - tag_menu() ?> - -
    - - sidebarvisible=="left"): ?> - ' ?> - sidebarvisible=="none"): ?> - - ' ?> - - - page_subtype != "login") and ($theme->page_subtype != "reauthenticate") and ($theme->sidebarvisible != "none")): ?> - - - sidebarvisible != "none")? "
    " : null ?> - - sidebarvisible == "left"): ?> - ' ?> - sidebarvisible == "none"): ?> - ' ?> - - ' ?> - - -
    - - - -page_bottom() ?> - - diff --git a/3.1/themes/greydragon/views/paginator.html.php b/3.1/themes/greydragon/views/paginator.html.php deleted file mode 100644 index 9b5e725e..00000000 --- a/3.1/themes/greydragon/views/paginator.html.php +++ /dev/null @@ -1,188 +0,0 @@ - - - -parent(); - endif; - $current_page = $page; - $total_pages = $max_pages; - // Prepare page url list - 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++): - $_pagelist[$i] = $siblings[$i-1]->url(); - endfor; - break; - default: - $current_page = 1; - $total_pages = 1; - $_pagelist[1] = url::site(); - break; - } - - if ($total_pages <= 1): - $pagination_msg = " "; - else: - $pagination_msg = t("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) . ''; - 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) . ''; - endif; - if ($i < 10): - $pagination_msg .= '·'; - endif; - endfor; - - $pagination_msg .= '…'; - $pagination_msg .= '' . t($total_pages - 1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t($total_pages) . ''; - - elseif ($current_page > $total_pages - 8): - $pagination_msg .= '' . t(1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; - $pagination_msg .= '…'; - - for ($i = $total_pages - 9; $i <= $total_pages; $i++): - if ($i == $current_page): - $pagination_msg .= '' . t($i) . ''; - else: - $pagination_msg .= '' . t($i) . ''; - endif; - if ($i < $total_pages): - $pagination_msg .= '·'; - endif; - endfor; - - else: - $pagination_msg .= '' . t(1) . ''; - $pagination_msg .= '·'; - $pagination_msg .= '' . t(2) . ''; - $pagination_msg .= '…'; - - for ($i = $current_page - 5; $i <= $current_page + 5; $i++): - if ($i == $current_page): - $pagination_msg .= '' . t($i) . ''; - 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 .= '·'; - $pagination_msg .= '' . t($total_pages) . ''; - endif; - endif; -?> - - \ No newline at end of file diff --git a/3.1/themes/greydragon/views/photo.html.php b/3.1/themes/greydragon/views/photo.html.php deleted file mode 100644 index 884e30a5..00000000 --- a/3.1/themes/greydragon/views/photo.html.php +++ /dev/null @@ -1,79 +0,0 @@ - -desc_allowbbcode): ?> - bb2html($item->description, 1); ?> - - description)); ?> - - -is_photometa_visible): ?> -' . $theme->thumb_info($item) . ''; ?> - - -
    - 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/3.1/themes/greydragon/views/rss_block.html.php b/3.1/themes/greydragon/views/rss_block.html.php deleted file mode 100644 index 4d30ce59..00000000 --- a/3.1/themes/greydragon/views/rss_block.html.php +++ /dev/null @@ -1,13 +0,0 @@ - - diff --git a/3.1/themes/greydragon/views/search.html.php b/3.1/themes/greydragon/views/search.html.php deleted file mode 100644 index 94fc170c..00000000 --- a/3.1/themes/greydragon/views/search.html.php +++ /dev/null @@ -1,43 +0,0 @@ - -
    -

    $q)) ?>

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

     

    -

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

    - - -
    \ No newline at end of file diff --git a/3.1/themes/greydragon/views/sidebar.html.php b/3.1/themes/greydragon/views/sidebar.html.php deleted file mode 100644 index 0cad333d..00000000 --- a/3.1/themes/greydragon/views/sidebar.html.php +++ /dev/null @@ -1,8 +0,0 @@ - - -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/3.1/themes/greydragon/views/tag_block.html.php b/3.1/themes/greydragon/views/tag_block.html.php deleted file mode 100644 index f9bc5886..00000000 --- a/3.1/themes/greydragon/views/tag_block.html.php +++ /dev/null @@ -1,27 +0,0 @@ - - -
    "> - -
    - \ No newline at end of file diff --git a/3.1/themes/greydragon/views/user_profile.html.php b/3.1/themes/greydragon/views/user_profile.html.php deleted file mode 100644 index b7d92f40..00000000 --- a/3.1/themes/greydragon/views/user_profile.html.php +++ /dev/null @@ -1,50 +0,0 @@ - - - -
    -

    $user->display_name())) ?>

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

    title) ?>

    -
    - view ?> -
    -
    - -
    From aa3fa8858e3cc51280ab87313855172d5e3ea514 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 26 Jan 2011 19:50:12 -0700 Subject: [PATCH 263/300] Removed ui-icon-link css class for bitly site_menu item, it's not used. --- 3.1/modules/bitly/helpers/bitly_event.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/bitly/helpers/bitly_event.php b/3.1/modules/bitly/helpers/bitly_event.php index 62caf6b7..1efeb264 100644 --- a/3.1/modules/bitly/helpers/bitly_event.php +++ b/3.1/modules/bitly/helpers/bitly_event.php @@ -36,7 +36,7 @@ class bitly_event_Core { ->label(t("Shorten link with bit.ly")) ->url(url::site("bitly/shorten/{$theme->item->id}?csrf={$theme->csrf}")) ->css_id("g-bitly-shorten") - ->css_class("g-bitly-shorten ui-icon-link")); + ->css_class("g-bitly-shorten")); } } From 5f2fe5a1df41fb7abd16b6f7af574e238e9ddeca Mon Sep 17 00:00:00 2001 From: rWatcher Date: Fri, 28 Jan 2011 10:53:57 -0500 Subject: [PATCH 264/300] Synced rWInfo with recent changes to info module. --- 3.0/modules/rwinfo/helpers/rwinfo_block.php | 65 ++++++++++++++++++- .../rwinfo/helpers/rwinfo_installer.php | 41 ++++++++++++ 3.0/modules/rwinfo/helpers/rwinfo_theme.php | 2 +- 3.0/modules/rwinfo/module.info | 2 +- 3.0/modules/rwinfo/notes.txt | 11 ++-- .../rwinfo/views/rwinfo_block.html.php | 50 +------------- 3.1/modules/rwinfo/helpers/rwinfo_block.php | 65 ++++++++++++++++++- .../rwinfo/helpers/rwinfo_installer.php | 41 ++++++++++++ 3.1/modules/rwinfo/helpers/rwinfo_theme.php | 2 +- 3.1/modules/rwinfo/module.info | 2 +- 3.1/modules/rwinfo/notes.txt | 11 ++-- .../rwinfo/views/rwinfo_block.html.php | 50 +------------- 12 files changed, 226 insertions(+), 116 deletions(-) create mode 100644 3.0/modules/rwinfo/helpers/rwinfo_installer.php create mode 100644 3.1/modules/rwinfo/helpers/rwinfo_installer.php diff --git a/3.0/modules/rwinfo/helpers/rwinfo_block.php b/3.0/modules/rwinfo/helpers/rwinfo_block.php index 7befb828..378d7a84 100644 --- a/3.0/modules/rwinfo/helpers/rwinfo_block.php +++ b/3.0/modules/rwinfo/helpers/rwinfo_block.php @@ -35,9 +35,9 @@ class rwinfo_block_Core { $block = new Block(); $block->css_id = "g-metadata"; - + // rWatcher Edit: Add Movie Info Option - //$block->title = $theme->item()->is_album() ? t("Album Info") : t("Photo Info"); + //$block->title = $theme->item()->is_album() ? t("Album info") : t("Photo info"); $block_title = ""; if ($theme->item->is_album()) { $block_title = t("Album Info"); @@ -48,9 +48,68 @@ class rwinfo_block_Core { } $block->title = $block_title; // End rWatcher Edit - + // rWatcher Edit: File Name change. $block->content = new View("rwinfo_block.html"); + + if ($theme->item->title && module::get_var("rwinfo", "show_title")) { + $info["title"] = array( + "label" => t("Title:"), + "value" => html::purify($theme->item->title) + ); + } + if ($theme->item->description && module::get_var("rwinfo", "show_description")) { + $info["description"] = array( + "label" => t("Description:"), + "value" => nl2br(html::purify($theme->item->description)) + ); + } + if (!$theme->item->is_album() && module::get_var("rwinfo", "show_name")) { + $info["file_name"] = array( + "label" => t("File name:"), + "value" => html::clean($theme->item->name) + ); + } + + // rWatcher Edit: + //if ($theme->item->captured && module::get_var("rwinfo", "show_captured")) { + // $info["captured"] = array( + // "label" => t("Captured:"), + // "value" => gallery::date_time($theme->item->captured) + // ); + //} + if ($theme->item->is_album() && $theme->item->created && module::get_var("rwinfo", "show_captured")) { + $info["captured"] = array( + "label" => t("Date:"), + "value" => gallery::date($theme->item->created) + ); + } + if (!$theme->item->is_album() && $theme->item->created && module::get_var("rwinfo", "show_captured")) { + $info["captured"] = array( + "label" => t("Date:"), + "value" => gallery::date_time($theme->item->captured) + ); + } + // End rWatcher Edit + + if ($theme->item->owner && module::get_var("rwinfo", "show_owner")) { + $display_name = $theme->item->owner->display_name(); + if ($theme->item->owner->url) { + $info["owner"] = array( + "label" => t("Owner:"), + "value" => "item->owner->url}\">" . + html::clean($display_name) . "" + ); + } else { + $info["owner"] = array( + "label" => t("Owner:"), + "value" => html::clean($display_name) + ); + } + } + $block->content->metadata = $info; + + module::event("info_block_get_metadata", $block, $theme->item); } break; } diff --git a/3.0/modules/rwinfo/helpers/rwinfo_installer.php b/3.0/modules/rwinfo/helpers/rwinfo_installer.php new file mode 100644 index 00000000..ca8b429d --- /dev/null +++ b/3.0/modules/rwinfo/helpers/rwinfo_installer.php @@ -0,0 +1,41 @@ +"; } - // rWatcher Edit: Display Tags + // rWatcher Edit: Display Tags on Thumbnails if (module::is_active("tag")) { $tags = ORM::factory("tag") ->join("items_tags", "tags.id", "items_tags.tag_id") diff --git a/3.0/modules/rwinfo/module.info b/3.0/modules/rwinfo/module.info index 7bbf8e37..684a31a0 100644 --- a/3.0/modules/rwinfo/module.info +++ b/3.0/modules/rwinfo/module.info @@ -1,3 +1,3 @@ name = "rWInfo" description = "Display extra information about photos and albums" -version = 1 +version = 2 diff --git a/3.0/modules/rwinfo/notes.txt b/3.0/modules/rwinfo/notes.txt index 74826f18..5085b0d1 100644 --- a/3.0/modules/rwinfo/notes.txt +++ b/3.0/modules/rwinfo/notes.txt @@ -1,11 +1,10 @@ -Drop Title and Description (they're displayed elsewhere in the default theme, no reason to show them twice on the same page) +Turn Title and Description off by Default in the installer.(they're displayed elsewhere in the default theme, no reason to show them twice on the same page) Hide the info sidebar for the root album (without title and description there really isn't anything worth displaying here) -Display date created for albums only (and continue to display the capture date for everything else) -Display tags in the info sidebar (if the tags module is active). Display tags when mousing over the thumbnails (if tags module is active). Display "Movie Info" on movies instead of "Photo Info" like the Gallery Info module does. -Use long month instead of short month on the album display. -Change Date/Time format to "F j, Y h:i:s a" for photos. - Changed block name on sidebar to rWInfo, to make it stick out more (helpers\rwinfo_block.php -> return array("metadata" => t("rWInfo")); +Change label for "captured" to "Date", display "created" for albums, "captured" for everything else. +Use gallery::date for formating the albums Date, and gallery::date_time for everything else. (I only want to see the Date an album was created, the time doesn't matter). +Change all occurences of get_var("info" to get_var("rwinfo" to avoid conflicts with the original info module. + diff --git a/3.0/modules/rwinfo/views/rwinfo_block.html.php b/3.0/modules/rwinfo/views/rwinfo_block.html.php index 32891268..b296fa1d 100644 --- a/3.0/modules/rwinfo/views/rwinfo_block.html.php +++ b/3.0/modules/rwinfo/views/rwinfo_block.html.php @@ -1,52 +1,8 @@ - diff --git a/3.1/modules/rwinfo/helpers/rwinfo_block.php b/3.1/modules/rwinfo/helpers/rwinfo_block.php index 7befb828..378d7a84 100644 --- a/3.1/modules/rwinfo/helpers/rwinfo_block.php +++ b/3.1/modules/rwinfo/helpers/rwinfo_block.php @@ -35,9 +35,9 @@ class rwinfo_block_Core { $block = new Block(); $block->css_id = "g-metadata"; - + // rWatcher Edit: Add Movie Info Option - //$block->title = $theme->item()->is_album() ? t("Album Info") : t("Photo Info"); + //$block->title = $theme->item()->is_album() ? t("Album info") : t("Photo info"); $block_title = ""; if ($theme->item->is_album()) { $block_title = t("Album Info"); @@ -48,9 +48,68 @@ class rwinfo_block_Core { } $block->title = $block_title; // End rWatcher Edit - + // rWatcher Edit: File Name change. $block->content = new View("rwinfo_block.html"); + + if ($theme->item->title && module::get_var("rwinfo", "show_title")) { + $info["title"] = array( + "label" => t("Title:"), + "value" => html::purify($theme->item->title) + ); + } + if ($theme->item->description && module::get_var("rwinfo", "show_description")) { + $info["description"] = array( + "label" => t("Description:"), + "value" => nl2br(html::purify($theme->item->description)) + ); + } + if (!$theme->item->is_album() && module::get_var("rwinfo", "show_name")) { + $info["file_name"] = array( + "label" => t("File name:"), + "value" => html::clean($theme->item->name) + ); + } + + // rWatcher Edit: + //if ($theme->item->captured && module::get_var("rwinfo", "show_captured")) { + // $info["captured"] = array( + // "label" => t("Captured:"), + // "value" => gallery::date_time($theme->item->captured) + // ); + //} + if ($theme->item->is_album() && $theme->item->created && module::get_var("rwinfo", "show_captured")) { + $info["captured"] = array( + "label" => t("Date:"), + "value" => gallery::date($theme->item->created) + ); + } + if (!$theme->item->is_album() && $theme->item->created && module::get_var("rwinfo", "show_captured")) { + $info["captured"] = array( + "label" => t("Date:"), + "value" => gallery::date_time($theme->item->captured) + ); + } + // End rWatcher Edit + + if ($theme->item->owner && module::get_var("rwinfo", "show_owner")) { + $display_name = $theme->item->owner->display_name(); + if ($theme->item->owner->url) { + $info["owner"] = array( + "label" => t("Owner:"), + "value" => "item->owner->url}\">" . + html::clean($display_name) . "" + ); + } else { + $info["owner"] = array( + "label" => t("Owner:"), + "value" => html::clean($display_name) + ); + } + } + $block->content->metadata = $info; + + module::event("info_block_get_metadata", $block, $theme->item); } break; } diff --git a/3.1/modules/rwinfo/helpers/rwinfo_installer.php b/3.1/modules/rwinfo/helpers/rwinfo_installer.php new file mode 100644 index 00000000..ca8b429d --- /dev/null +++ b/3.1/modules/rwinfo/helpers/rwinfo_installer.php @@ -0,0 +1,41 @@ +"; } - // rWatcher Edit: Display Tags + // rWatcher Edit: Display Tags on Thumbnails if (module::is_active("tag")) { $tags = ORM::factory("tag") ->join("items_tags", "tags.id", "items_tags.tag_id") diff --git a/3.1/modules/rwinfo/module.info b/3.1/modules/rwinfo/module.info index 7bbf8e37..684a31a0 100644 --- a/3.1/modules/rwinfo/module.info +++ b/3.1/modules/rwinfo/module.info @@ -1,3 +1,3 @@ name = "rWInfo" description = "Display extra information about photos and albums" -version = 1 +version = 2 diff --git a/3.1/modules/rwinfo/notes.txt b/3.1/modules/rwinfo/notes.txt index 74826f18..5085b0d1 100644 --- a/3.1/modules/rwinfo/notes.txt +++ b/3.1/modules/rwinfo/notes.txt @@ -1,11 +1,10 @@ -Drop Title and Description (they're displayed elsewhere in the default theme, no reason to show them twice on the same page) +Turn Title and Description off by Default in the installer.(they're displayed elsewhere in the default theme, no reason to show them twice on the same page) Hide the info sidebar for the root album (without title and description there really isn't anything worth displaying here) -Display date created for albums only (and continue to display the capture date for everything else) -Display tags in the info sidebar (if the tags module is active). Display tags when mousing over the thumbnails (if tags module is active). Display "Movie Info" on movies instead of "Photo Info" like the Gallery Info module does. -Use long month instead of short month on the album display. -Change Date/Time format to "F j, Y h:i:s a" for photos. - Changed block name on sidebar to rWInfo, to make it stick out more (helpers\rwinfo_block.php -> return array("metadata" => t("rWInfo")); +Change label for "captured" to "Date", display "created" for albums, "captured" for everything else. +Use gallery::date for formating the albums Date, and gallery::date_time for everything else. (I only want to see the Date an album was created, the time doesn't matter). +Change all occurences of get_var("info" to get_var("rwinfo" to avoid conflicts with the original info module. + diff --git a/3.1/modules/rwinfo/views/rwinfo_block.html.php b/3.1/modules/rwinfo/views/rwinfo_block.html.php index 32891268..b296fa1d 100644 --- a/3.1/modules/rwinfo/views/rwinfo_block.html.php +++ b/3.1/modules/rwinfo/views/rwinfo_block.html.php @@ -1,52 +1,8 @@ - From 73bb2f52489a72112e8812faf8a38c0405a21554 Mon Sep 17 00:00:00 2001 From: Carlos Saltos Date: Sat, 29 Jan 2011 13:20:17 +0100 Subject: [PATCH 265/300] adding filename and tag editing support for captionation (gallery 3.1) --- 3.1/modules/captionator/controllers/captionator.php | 4 ++++ 3.1/modules/captionator/views/captionator_dialog.html.php | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/3.1/modules/captionator/controllers/captionator.php b/3.1/modules/captionator/controllers/captionator.php index 54d262f8..8b380f66 100644 --- a/3.1/modules/captionator/controllers/captionator.php +++ b/3.1/modules/captionator/controllers/captionator.php @@ -54,6 +54,8 @@ class Captionator_Controller extends Controller { if (Input::instance()->post("save")) { $titles = Input::instance()->post("title"); $descriptions = Input::instance()->post("description"); + $filenames = Input::instance()->post("filename"); + $internetaddresses = Input::instance()->post("internetaddress"); $tags = Input::instance()->post("tags"); $enable_tags = module::is_active("tag"); foreach (array_keys($titles) as $id) { @@ -61,6 +63,8 @@ class Captionator_Controller extends Controller { if ($item->loaded() && access::can("edit", $item)) { $item->title = $titles[$id]; $item->description = $descriptions[$id]; + $item->name = $filenames[$id]; + $item->slug = $internetaddresses[$id]; $item->save(); if ($enable_tags) { tag::clear_all($item); diff --git a/3.1/modules/captionator/views/captionator_dialog.html.php b/3.1/modules/captionator/views/captionator_dialog.html.php index 317873ee..fe77c2ab 100644 --- a/3.1/modules/captionator/views/captionator_dialog.html.php +++ b/3.1/modules/captionator/views/captionator_dialog.html.php @@ -35,6 +35,14 @@ +
  • + + +
  • +
  • + + +
  • From f047b7a675968261a2de6ad762c4af29c3583999 Mon Sep 17 00:00:00 2001 From: brentil Date: Sun, 23 Jan 2011 22:05:53 -0500 Subject: [PATCH 266/300] brentil's fix for array out of range errors with unset on some installations with v7 --- .../views/admin_moduleupdates.html.php | 36 ++++++++++--------- .../views/admin_moduleupdates.html.php | 36 ++++++++++--------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php index ea3fa7f7..e55ed7af 100644 --- a/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.0/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -35,15 +35,15 @@
    - $module_name): ?> - - "> - - "; ?> *"; } ?> "; } ?> - "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> - - - + + + "> + + "; ?> *"; } ?> "; } ?> + "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> + + + @@ -57,14 +57,16 @@
    - $module_name): ?> - "> - - "; ?> *"; } ?> "; } ?> - "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> - "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?> - - + + + "> + + "; ?> *"; } ?> "; } ?> + "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> + "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?> + + + diff --git a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php index ea3fa7f7..e55ed7af 100644 --- a/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php +++ b/3.1/modules/moduleupdates/views/admin_moduleupdates.html.php @@ -35,15 +35,15 @@
    - $module_name): ?> - - "> - - "; ?> *"; } ?> "; } ?> - "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> - - - + + + "> + + "; ?> *"; } ?> "; } ?> + "; ?> *"; } ?> $module_name['code_version']) { echo "".$module_name['core_version']."";} else { echo $module_name['core_version']; } ?> "; } ?> + + + @@ -57,14 +57,16 @@
    - $module_name): ?> - "> - - "; ?> *"; } ?> "; } ?> - "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> - "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?> - - + + + "> + + "; ?> *"; } ?> "; } ?> + "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['contrib_version']."";} else { echo $module_name['contrib_version']; } ?> "; } ?> + "; ?> *"; } ?> $module_name['version'] or $module_name['core_version'] > $module_name['code_version']) { echo "".$module_name['gh_version']."";} else { echo $module_name['gh_version']; } ?> "; } ?> + + + From 1cd2de2154a952eb01eb584e2a0c4e502880cb51 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Mon, 31 Jan 2011 14:21:04 -0500 Subject: [PATCH 267/300] Added warning message that TagsInAlbum requires Tags. --- .../tagsinalbum/helpers/tagsinalbum_event.php | 37 +++++++++++++++++++ .../helpers/tagsinalbum_installer.php | 36 ++++++++++++++++++ .../tagsinalbum/helpers/tagsinalbum_event.php | 37 +++++++++++++++++++ .../helpers/tagsinalbum_installer.php | 36 ++++++++++++++++++ 4 files changed, 146 insertions(+) create mode 100644 3.0/modules/tagsinalbum/helpers/tagsinalbum_event.php create mode 100644 3.0/modules/tagsinalbum/helpers/tagsinalbum_installer.php create mode 100644 3.1/modules/tagsinalbum/helpers/tagsinalbum_event.php create mode 100644 3.1/modules/tagsinalbum/helpers/tagsinalbum_installer.php diff --git a/3.0/modules/tagsinalbum/helpers/tagsinalbum_event.php b/3.0/modules/tagsinalbum/helpers/tagsinalbum_event.php new file mode 100644 index 00000000..fe7339ae --- /dev/null +++ b/3.0/modules/tagsinalbum/helpers/tagsinalbum_event.php @@ -0,0 +1,37 @@ +module == "tag") { + $data->messages["warn"][] = t("The Tags In Album module requires the Tags module."); + } + } + + static function module_change($changes) { + if (!module::is_active("tag") || in_array("tag", $changes->deactivate)) { + site_status::warning( + t("The Tags In Album module requires the Tags module. Activate the Tags module now", + array("url" => html::mark_clean(url::site("admin/modules")))), + "tagsinalbum_needs_tag"); + } else { + site_status::clear("tagsinalbum_needs_tag"); + } + } +} diff --git a/3.0/modules/tagsinalbum/helpers/tagsinalbum_installer.php b/3.0/modules/tagsinalbum/helpers/tagsinalbum_installer.php new file mode 100644 index 00000000..4357ab87 --- /dev/null +++ b/3.0/modules/tagsinalbum/helpers/tagsinalbum_installer.php @@ -0,0 +1,36 @@ +module == "tag") { + $data->messages["warn"][] = t("The Tags In Album module requires the Tags module."); + } + } + + static function module_change($changes) { + if (!module::is_active("tag") || in_array("tag", $changes->deactivate)) { + site_status::warning( + t("The Tags In Album module requires the Tags module. Activate the Tags module now", + array("url" => html::mark_clean(url::site("admin/modules")))), + "tagsinalbum_needs_tag"); + } else { + site_status::clear("tagsinalbum_needs_tag"); + } + } +} diff --git a/3.1/modules/tagsinalbum/helpers/tagsinalbum_installer.php b/3.1/modules/tagsinalbum/helpers/tagsinalbum_installer.php new file mode 100644 index 00000000..4357ab87 --- /dev/null +++ b/3.1/modules/tagsinalbum/helpers/tagsinalbum_installer.php @@ -0,0 +1,36 @@ + Date: Wed, 2 Feb 2011 14:32:54 -0500 Subject: [PATCH 268/300] Create a new table to track all protected files. --- .../controllers/albumpassword.php | 37 +++++++++-- .../albumpassword/helpers/MY_access.php | 27 +++----- 3.0/modules/albumpassword/helpers/MY_item.php | 19 ++++-- .../helpers/albumpassword_event.php | 61 +++++++++++++++---- .../helpers/albumpassword_installer.php | 34 ++++++++--- .../models/albumpassword_idcache.php | 21 +++++++ 3.0/modules/albumpassword/module.info | 2 +- .../controllers/albumpassword.php | 37 +++++++++-- .../albumpassword/helpers/MY_access.php | 27 +++----- 3.1/modules/albumpassword/helpers/MY_item.php | 19 ++++-- .../helpers/albumpassword_event.php | 61 +++++++++++++++---- .../helpers/albumpassword_installer.php | 34 ++++++++--- .../models/albumpassword_idcache.php | 21 +++++++ 3.1/modules/albumpassword/module.info | 2 +- 14 files changed, 306 insertions(+), 96 deletions(-) create mode 100644 3.0/modules/albumpassword/models/albumpassword_idcache.php create mode 100644 3.1/modules/albumpassword/models/albumpassword_idcache.php diff --git a/3.0/modules/albumpassword/controllers/albumpassword.php b/3.0/modules/albumpassword/controllers/albumpassword.php index e2336f47..dfedcd2c 100644 --- a/3.0/modules/albumpassword/controllers/albumpassword.php +++ b/3.0/modules/albumpassword/controllers/albumpassword.php @@ -49,9 +49,12 @@ class albumpassword_Controller extends Controller { access::required("view", $item); access::required("edit", $item); - // Check for and delete the password. - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $id)->find(); - if ($existing_password->loaded()) { + // Check for and delete the password and any cached ids assigned to it. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $id)->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + db::build()->delete("albumpassword_idcaches")->where("password_id", "=", $one_password->id)->execute(); + } db::build()->delete("items_albumpasswords")->where("album_id", "=", $id)->execute(); message::success(t("Password Removed.")); } @@ -70,9 +73,12 @@ class albumpassword_Controller extends Controller { $album_id = Input::instance()->post("item_id"); $album_password = Input::instance()->post("assignpassword_password"); - // Check for, and remove, any existing passwords. - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_id)->find(); - if ($existing_password->loaded()) { + // Check for, and remove, any existing passwords and cached ids. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_id)->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + db::build()->delete("albumpassword_idcaches")->where("password_id", "=", $one_password->id)->execute(); + } db::build()->delete("items_albumpasswords")->where("album_id", "=", $album_id)->execute(); } @@ -82,6 +88,25 @@ class albumpassword_Controller extends Controller { $new_password->password = $album_password; $new_password->save(); + // Add the album to the id cache. + $cached_album = ORM::factory("albumpassword_idcache"); + $cached_album->password_id = $new_password->id; + $cached_album->item_id = $album_id; + $cached_album->save(); + + // Check for any sub-items within the album, add all of them to the id cache. + $items = ORM::factory("item", $album_id) + ->viewable() + ->descendants(); + if (count($items) > 0) { + foreach ($items as $one_item) { + $cached_item = ORM::factory("albumpassword_idcache"); + $cached_item->password_id = $new_password->id; + $cached_item->item_id = $one_item->id; + $cached_item->save(); + } + } + // Display a success message and close the dialog. message::success(t("Password saved.")); print "\n\n\n\n\n"; diff --git a/3.0/modules/albumpassword/helpers/MY_access.php b/3.0/modules/albumpassword/helpers/MY_access.php index 339426b6..38b48fc3 100644 --- a/3.0/modules/albumpassword/helpers/MY_access.php +++ b/3.0/modules/albumpassword/helpers/MY_access.php @@ -21,38 +21,29 @@ class access extends access_Core { static function required($perm_name, $item) { // Original code from the required function in modules/gallery/helpers/access.php. - if (!self::can($perm_name, $item)) { + if (!access::can($perm_name, $item)) { if ($perm_name == "view") { // Treat as if the item didn't exist, don't leak any information. throw new Kohana_404_Exception(); } else { - self::forbidden(); + access::forbidden(); } // Begin rWatcher modifications. // Throw a 404 error when a user attempts to access a protected item, - // unless the password has been provided, or the user is the item's owner. + // unless the password has been provided, or the user is the item's owner. } elseif (module::get_var("albumpassword", "hideonly") == false) { - $album_item = ""; - do { - if ($album_item == "") { - if ($item->is_album()) { - $album_item = $item; - } else { - $album_item = $item->parent(); - } - } else { - $album_item = $album_item->parent(); - } - - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_item->id)->find(); + $item_protected = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + if (count($item_protected) > 0) { + $existing_password = ORM::factory("items_albumpassword")->where("id", "=", $item_protected[0]->password_id)->find(); if ($existing_password->loaded()) { if ((cookie::get("g3_albumpassword") != $existing_password->password) && - (identity::active_user()->id != $album_item->owner_id)) { + (identity::active_user()->id != $item->owner_id) && + (!identity::active_user()->admin)) { throw new Kohana_404_Exception(); } } - } while ($album_item->parent_id > 0); + } } } } diff --git a/3.0/modules/albumpassword/helpers/MY_item.php b/3.0/modules/albumpassword/helpers/MY_item.php index 1af98cdc..e26b65c6 100644 --- a/3.0/modules/albumpassword/helpers/MY_item.php +++ b/3.0/modules/albumpassword/helpers/MY_item.php @@ -29,10 +29,21 @@ class item extends item_Core { // If not, hide whatever is restricted by an album password // that the current user is not the owner of. if (!identity::active_user()->admin) { - $model->and_open()->join("items_albumpasswords", "items.id", "items_albumpasswords.album_id", "LEFT OUTER") - ->and_where("items_albumpasswords.album_id", "IS", NULL) - ->or_where("items_albumpasswords.password", "=", cookie::get("g3_albumpassword")) - ->or_where("items.owner_id", "=", identity::active_user()->id)->close(); + + // Display items that are not in idcaches. + $model->and_open()->join("albumpassword_idcaches", "items.id", "albumpassword_idcaches.item_id", "LEFT OUTER") + ->and_where("albumpassword_idcaches.item_id", "IS", NULL); + + // ... Unless their password id corresponds with a valid password. + $existing_password = ORM::factory("items_albumpassword")->where("password", "=", cookie::get("g3_albumpassword"))->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + $model->or_where("albumpassword_idcaches.password_id", "=", $one_password->id); + } + } + + // Or the current user is the owner of the item. + $model->or_where("items.owner_id", "=", identity::active_user()->id)->close(); } return $model; diff --git a/3.0/modules/albumpassword/helpers/albumpassword_event.php b/3.0/modules/albumpassword/helpers/albumpassword_event.php index 72d549a4..cd981af1 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_event.php @@ -81,27 +81,64 @@ class albumpassword_event_Core { ->css_id("g-album-password-remove") ->url(url::site("albumpassword/remove/" . $item->id))); } elseif ($item->id != 1) { - $menu->get("options_menu") - ->append(Menu::factory("dialog") - ->id("albumpassword_assign") - ->label(t("Assign password")) - ->css_id("g-album-password-assign") - ->url(url::site("albumpassword/assign/" . $item->id))); + $passworded_subitems = ORM::factory("item", $item->id) + ->and_open()->join("albumpassword_idcaches", "items.id", "albumpassword_idcaches.item_id", "LEFT OUTER") + ->where("albumpassword_idcaches.item_id", "IS NOT", NULL)->close() + ->descendants(); + + $existing_cacheditem = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + if ((count($existing_cacheditem) == 0) && count($passworded_subitems) == 0) { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("albumpassword_assign") + ->label(t("Assign password")) + ->css_id("g-album-password-assign") + ->url(url::site("albumpassword/assign/" . $item->id))); + } } } } } static function item_deleted($item) { - // If an album is deleted, remove any associated passwords. - $existingPasswords = ORM::factory("items_albumpassword") - ->where("album_id", "=", $item->id) - ->find_all(); - if (count($existingPasswords) > 0) { - db::build()->delete("items_albumpassword")->where("album_id", "=", $item->id)->execute(); + // Check for and delete the password and any cached ids assigned to it. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $item->id)->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + db::build()->delete("albumpassword_idcaches")->where("password_id", "=", $one_password->id)->execute(); + } + db::build()->delete("items_albumpasswords")->where("album_id", "=", $item->id)->execute(); + message::success(t("Password Removed.")); + } else { + db::build()->delete("albumpassword_idcaches")->where("item_id", "=", $item->id)->execute(); } } + static function item_created($item) { + // Check for any already existing password on parent album(s), if found, generate cache data for the new item. + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + if (count($existing_password) > 0) { + $new_cachedid = ORM::factory("albumpassword_idcache"); + $new_cachedid->password_id = $existing_password[0]->password_id; + $new_cachedid->item_id = $item->id; + $new_cachedid->save(); + } + } + + static function item_moved($item, $old_parent) { + // Delete any existing cache data. + db::build()->delete("albumpassword_idcaches")->where("item_id", "=", $item->id)->execute(); + + // Check for a password on the new parent, generate cache data if necessary. + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + if (count($existing_password) > 0) { + $new_cachedid = ORM::factory("albumpassword_idcache"); + $new_cachedid->password_id = $existing_password[0]->password_id; + $new_cachedid->item_id = $item->id; + $new_cachedid->save(); + } + } + static function admin_menu($menu, $theme) { // Add a link to the Album Password admin page to the Content menu. $menu->get("settings_menu") diff --git a/3.0/modules/albumpassword/helpers/albumpassword_installer.php b/3.0/modules/albumpassword/helpers/albumpassword_installer.php index 075f252a..39b7ccee 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_installer.php @@ -28,25 +28,45 @@ class albumpassword_installer { PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); + // Create a table to store a list of all protected items in. + $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( + `id` int(9) NOT NULL auto_increment, + `password_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + // Set the default value for this module's behavior. module::set_var("albumpassword", "hideonly", true); // Set the module's version number. - module::set_version("albumpassword", 2); + module::set_version("albumpassword", 3); } static function upgrade($version) { - // Set the default value for this module's behavior. - module::set_var("albumpassword", "hideonly", true); - - // Set the module's version number. - module::set_version("albumpassword", 2); + $db = Database::instance(); + if ($version == 1) { + // Set the default value for this module's behavior. + module::set_var("albumpassword", "hideonly", true); + module::set_version("albumpassword", $version = 2); + } + if ($version == 2) { + // Create a table to store a list of all protected items in. + $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( + `id` int(9) NOT NULL auto_increment, + `password_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("albumpassword", $version = 3); + } } static function uninstall() { // Delete the password table before uninstalling. $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {items_albumpassword};"); + $db->query("DROP TABLE IF EXISTS {items_albumpasswords};"); + $db->query("DROP TABLE IF EXISTS {albumpassword_idcaches};"); module::delete("albumpassword"); } } diff --git a/3.0/modules/albumpassword/models/albumpassword_idcache.php b/3.0/modules/albumpassword/models/albumpassword_idcache.php new file mode 100644 index 00000000..e3d80667 --- /dev/null +++ b/3.0/modules/albumpassword/models/albumpassword_idcache.php @@ -0,0 +1,21 @@ +where("album_id", "=", $id)->find(); - if ($existing_password->loaded()) { + // Check for and delete the password and any cached ids assigned to it. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $id)->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + db::build()->delete("albumpassword_idcaches")->where("password_id", "=", $one_password->id)->execute(); + } db::build()->delete("items_albumpasswords")->where("album_id", "=", $id)->execute(); message::success(t("Password Removed.")); } @@ -70,9 +73,12 @@ class albumpassword_Controller extends Controller { $album_id = Input::instance()->post("item_id"); $album_password = Input::instance()->post("assignpassword_password"); - // Check for, and remove, any existing passwords. - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_id)->find(); - if ($existing_password->loaded()) { + // Check for, and remove, any existing passwords and cached ids. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_id)->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + db::build()->delete("albumpassword_idcaches")->where("password_id", "=", $one_password->id)->execute(); + } db::build()->delete("items_albumpasswords")->where("album_id", "=", $album_id)->execute(); } @@ -82,6 +88,25 @@ class albumpassword_Controller extends Controller { $new_password->password = $album_password; $new_password->save(); + // Add the album to the id cache. + $cached_album = ORM::factory("albumpassword_idcache"); + $cached_album->password_id = $new_password->id; + $cached_album->item_id = $album_id; + $cached_album->save(); + + // Check for any sub-items within the album, add all of them to the id cache. + $items = ORM::factory("item", $album_id) + ->viewable() + ->descendants(); + if (count($items) > 0) { + foreach ($items as $one_item) { + $cached_item = ORM::factory("albumpassword_idcache"); + $cached_item->password_id = $new_password->id; + $cached_item->item_id = $one_item->id; + $cached_item->save(); + } + } + // Display a success message and close the dialog. message::success(t("Password saved.")); print "\n\n\n\n\n"; diff --git a/3.1/modules/albumpassword/helpers/MY_access.php b/3.1/modules/albumpassword/helpers/MY_access.php index 339426b6..38b48fc3 100644 --- a/3.1/modules/albumpassword/helpers/MY_access.php +++ b/3.1/modules/albumpassword/helpers/MY_access.php @@ -21,38 +21,29 @@ class access extends access_Core { static function required($perm_name, $item) { // Original code from the required function in modules/gallery/helpers/access.php. - if (!self::can($perm_name, $item)) { + if (!access::can($perm_name, $item)) { if ($perm_name == "view") { // Treat as if the item didn't exist, don't leak any information. throw new Kohana_404_Exception(); } else { - self::forbidden(); + access::forbidden(); } // Begin rWatcher modifications. // Throw a 404 error when a user attempts to access a protected item, - // unless the password has been provided, or the user is the item's owner. + // unless the password has been provided, or the user is the item's owner. } elseif (module::get_var("albumpassword", "hideonly") == false) { - $album_item = ""; - do { - if ($album_item == "") { - if ($item->is_album()) { - $album_item = $item; - } else { - $album_item = $item->parent(); - } - } else { - $album_item = $album_item->parent(); - } - - $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $album_item->id)->find(); + $item_protected = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + if (count($item_protected) > 0) { + $existing_password = ORM::factory("items_albumpassword")->where("id", "=", $item_protected[0]->password_id)->find(); if ($existing_password->loaded()) { if ((cookie::get("g3_albumpassword") != $existing_password->password) && - (identity::active_user()->id != $album_item->owner_id)) { + (identity::active_user()->id != $item->owner_id) && + (!identity::active_user()->admin)) { throw new Kohana_404_Exception(); } } - } while ($album_item->parent_id > 0); + } } } } diff --git a/3.1/modules/albumpassword/helpers/MY_item.php b/3.1/modules/albumpassword/helpers/MY_item.php index 1af98cdc..e26b65c6 100644 --- a/3.1/modules/albumpassword/helpers/MY_item.php +++ b/3.1/modules/albumpassword/helpers/MY_item.php @@ -29,10 +29,21 @@ class item extends item_Core { // If not, hide whatever is restricted by an album password // that the current user is not the owner of. if (!identity::active_user()->admin) { - $model->and_open()->join("items_albumpasswords", "items.id", "items_albumpasswords.album_id", "LEFT OUTER") - ->and_where("items_albumpasswords.album_id", "IS", NULL) - ->or_where("items_albumpasswords.password", "=", cookie::get("g3_albumpassword")) - ->or_where("items.owner_id", "=", identity::active_user()->id)->close(); + + // Display items that are not in idcaches. + $model->and_open()->join("albumpassword_idcaches", "items.id", "albumpassword_idcaches.item_id", "LEFT OUTER") + ->and_where("albumpassword_idcaches.item_id", "IS", NULL); + + // ... Unless their password id corresponds with a valid password. + $existing_password = ORM::factory("items_albumpassword")->where("password", "=", cookie::get("g3_albumpassword"))->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + $model->or_where("albumpassword_idcaches.password_id", "=", $one_password->id); + } + } + + // Or the current user is the owner of the item. + $model->or_where("items.owner_id", "=", identity::active_user()->id)->close(); } return $model; diff --git a/3.1/modules/albumpassword/helpers/albumpassword_event.php b/3.1/modules/albumpassword/helpers/albumpassword_event.php index 72d549a4..cd981af1 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_event.php @@ -81,27 +81,64 @@ class albumpassword_event_Core { ->css_id("g-album-password-remove") ->url(url::site("albumpassword/remove/" . $item->id))); } elseif ($item->id != 1) { - $menu->get("options_menu") - ->append(Menu::factory("dialog") - ->id("albumpassword_assign") - ->label(t("Assign password")) - ->css_id("g-album-password-assign") - ->url(url::site("albumpassword/assign/" . $item->id))); + $passworded_subitems = ORM::factory("item", $item->id) + ->and_open()->join("albumpassword_idcaches", "items.id", "albumpassword_idcaches.item_id", "LEFT OUTER") + ->where("albumpassword_idcaches.item_id", "IS NOT", NULL)->close() + ->descendants(); + + $existing_cacheditem = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + if ((count($existing_cacheditem) == 0) && count($passworded_subitems) == 0) { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("albumpassword_assign") + ->label(t("Assign password")) + ->css_id("g-album-password-assign") + ->url(url::site("albumpassword/assign/" . $item->id))); + } } } } } static function item_deleted($item) { - // If an album is deleted, remove any associated passwords. - $existingPasswords = ORM::factory("items_albumpassword") - ->where("album_id", "=", $item->id) - ->find_all(); - if (count($existingPasswords) > 0) { - db::build()->delete("items_albumpassword")->where("album_id", "=", $item->id)->execute(); + // Check for and delete the password and any cached ids assigned to it. + $existing_password = ORM::factory("items_albumpassword")->where("album_id", "=", $item->id)->find_all(); + if (count($existing_password) > 0) { + foreach ($existing_password as $one_password) { + db::build()->delete("albumpassword_idcaches")->where("password_id", "=", $one_password->id)->execute(); + } + db::build()->delete("items_albumpasswords")->where("album_id", "=", $item->id)->execute(); + message::success(t("Password Removed.")); + } else { + db::build()->delete("albumpassword_idcaches")->where("item_id", "=", $item->id)->execute(); } } + static function item_created($item) { + // Check for any already existing password on parent album(s), if found, generate cache data for the new item. + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + if (count($existing_password) > 0) { + $new_cachedid = ORM::factory("albumpassword_idcache"); + $new_cachedid->password_id = $existing_password[0]->password_id; + $new_cachedid->item_id = $item->id; + $new_cachedid->save(); + } + } + + static function item_moved($item, $old_parent) { + // Delete any existing cache data. + db::build()->delete("albumpassword_idcaches")->where("item_id", "=", $item->id)->execute(); + + // Check for a password on the new parent, generate cache data if necessary. + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + if (count($existing_password) > 0) { + $new_cachedid = ORM::factory("albumpassword_idcache"); + $new_cachedid->password_id = $existing_password[0]->password_id; + $new_cachedid->item_id = $item->id; + $new_cachedid->save(); + } + } + static function admin_menu($menu, $theme) { // Add a link to the Album Password admin page to the Content menu. $menu->get("settings_menu") diff --git a/3.1/modules/albumpassword/helpers/albumpassword_installer.php b/3.1/modules/albumpassword/helpers/albumpassword_installer.php index 075f252a..39b7ccee 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_installer.php @@ -28,25 +28,45 @@ class albumpassword_installer { PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); + // Create a table to store a list of all protected items in. + $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( + `id` int(9) NOT NULL auto_increment, + `password_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + // Set the default value for this module's behavior. module::set_var("albumpassword", "hideonly", true); // Set the module's version number. - module::set_version("albumpassword", 2); + module::set_version("albumpassword", 3); } static function upgrade($version) { - // Set the default value for this module's behavior. - module::set_var("albumpassword", "hideonly", true); - - // Set the module's version number. - module::set_version("albumpassword", 2); + $db = Database::instance(); + if ($version == 1) { + // Set the default value for this module's behavior. + module::set_var("albumpassword", "hideonly", true); + module::set_version("albumpassword", $version = 2); + } + if ($version == 2) { + // Create a table to store a list of all protected items in. + $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( + `id` int(9) NOT NULL auto_increment, + `password_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("albumpassword", $version = 3); + } } static function uninstall() { // Delete the password table before uninstalling. $db = Database::instance(); - $db->query("DROP TABLE IF EXISTS {items_albumpassword};"); + $db->query("DROP TABLE IF EXISTS {items_albumpasswords};"); + $db->query("DROP TABLE IF EXISTS {albumpassword_idcaches};"); module::delete("albumpassword"); } } diff --git a/3.1/modules/albumpassword/models/albumpassword_idcache.php b/3.1/modules/albumpassword/models/albumpassword_idcache.php new file mode 100644 index 00000000..e3d80667 --- /dev/null +++ b/3.1/modules/albumpassword/models/albumpassword_idcache.php @@ -0,0 +1,21 @@ + Date: Wed, 2 Feb 2011 23:51:32 -0500 Subject: [PATCH 269/300] Minor Bugfixes. --- 3.0/modules/exif_gps/controllers/exif_gps.php | 2 +- 3.0/modules/exif_gps/helpers/exif_gps_task.php | 10 ++++++---- 3.1/modules/exif_gps/controllers/exif_gps.php | 2 +- 3.1/modules/exif_gps/helpers/exif_gps_task.php | 10 ++++++---- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/3.0/modules/exif_gps/controllers/exif_gps.php b/3.0/modules/exif_gps/controllers/exif_gps.php index be6e92ac..52b94b9d 100644 --- a/3.0/modules/exif_gps/controllers/exif_gps.php +++ b/3.0/modules/exif_gps/controllers/exif_gps.php @@ -32,7 +32,7 @@ class EXIF_GPS_Controller extends Controller { ->order_by("exif_coordinates.latitude", "ASC") ->descendants(); $curr_album = ORM::factory("item")->where("id", "=", $type_id)->find_all(); - $map_title = $curr_album[0]->name; + $map_title = $curr_album[0]->title; } elseif ($map_type == "user") { // Generate an array of all items uploaded by the current user that // have exif gps coordinates and order by latitude (to group items diff --git a/3.0/modules/exif_gps/helpers/exif_gps_task.php b/3.0/modules/exif_gps/helpers/exif_gps_task.php index c3bcc73c..b07646ad 100644 --- a/3.0/modules/exif_gps/helpers/exif_gps_task.php +++ b/3.0/modules/exif_gps/helpers/exif_gps_task.php @@ -50,16 +50,18 @@ class exif_gps_task_Core { $completed = $task->get("completed"); // Generate an array of the next 100 photos to check. - $all_photos = ORM::factory("item") - ->where("id", ">", $last_id) - ->where("type", "=", "photo") - ->find_all(100); + //$all_photos = ORM::factory("item") + // ->where("id", ">", $last_id) + // ->where("type", "=", "photo") + // ->order_by("id") + // ->find_all(100); // Check each photo in the array to see if it already has exif gps data associated with it. // If it doesn't, attempt to extract gps coordinates. foreach (ORM::factory("item") ->where("id", ">", $last_id) ->where("type", "=", "photo") + ->order_by("id") ->find_all(100) as $item) { $record = ORM::factory("exif_coordinate")->where("item_id", "=", $item->id)->find(); diff --git a/3.1/modules/exif_gps/controllers/exif_gps.php b/3.1/modules/exif_gps/controllers/exif_gps.php index be6e92ac..52b94b9d 100644 --- a/3.1/modules/exif_gps/controllers/exif_gps.php +++ b/3.1/modules/exif_gps/controllers/exif_gps.php @@ -32,7 +32,7 @@ class EXIF_GPS_Controller extends Controller { ->order_by("exif_coordinates.latitude", "ASC") ->descendants(); $curr_album = ORM::factory("item")->where("id", "=", $type_id)->find_all(); - $map_title = $curr_album[0]->name; + $map_title = $curr_album[0]->title; } elseif ($map_type == "user") { // Generate an array of all items uploaded by the current user that // have exif gps coordinates and order by latitude (to group items diff --git a/3.1/modules/exif_gps/helpers/exif_gps_task.php b/3.1/modules/exif_gps/helpers/exif_gps_task.php index c3bcc73c..b07646ad 100644 --- a/3.1/modules/exif_gps/helpers/exif_gps_task.php +++ b/3.1/modules/exif_gps/helpers/exif_gps_task.php @@ -50,16 +50,18 @@ class exif_gps_task_Core { $completed = $task->get("completed"); // Generate an array of the next 100 photos to check. - $all_photos = ORM::factory("item") - ->where("id", ">", $last_id) - ->where("type", "=", "photo") - ->find_all(100); + //$all_photos = ORM::factory("item") + // ->where("id", ">", $last_id) + // ->where("type", "=", "photo") + // ->order_by("id") + // ->find_all(100); // Check each photo in the array to see if it already has exif gps data associated with it. // If it doesn't, attempt to extract gps coordinates. foreach (ORM::factory("item") ->where("id", ">", $last_id) ->where("type", "=", "photo") + ->order_by("id") ->find_all(100) as $item) { $record = ORM::factory("exif_coordinate")->where("item_id", "=", $item->id)->find(); From c6642aa9a20993b60c8dfbde376a7c83564bd2ea Mon Sep 17 00:00:00 2001 From: rWatcher Date: Thu, 3 Feb 2011 15:40:15 -0500 Subject: [PATCH 270/300] BugFix: Protected photos don't load properly. --- 3.0/modules/albumpassword/helpers/MY_access.php | 2 +- .../albumpassword/helpers/albumpassword_installer.php | 8 ++++---- 3.1/modules/albumpassword/helpers/MY_access.php | 2 +- .../albumpassword/helpers/albumpassword_installer.php | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/MY_access.php b/3.0/modules/albumpassword/helpers/MY_access.php index 38b48fc3..bda1db32 100644 --- a/3.0/modules/albumpassword/helpers/MY_access.php +++ b/3.0/modules/albumpassword/helpers/MY_access.php @@ -33,7 +33,7 @@ class access extends access_Core { // Throw a 404 error when a user attempts to access a protected item, // unless the password has been provided, or the user is the item's owner. } elseif (module::get_var("albumpassword", "hideonly") == false) { - $item_protected = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + $item_protected = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->order_by("cache_id")->find_all(); if (count($item_protected) > 0) { $existing_password = ORM::factory("items_albumpassword")->where("id", "=", $item_protected[0]->password_id)->find(); if ($existing_password->loaded()) { diff --git a/3.0/modules/albumpassword/helpers/albumpassword_installer.php b/3.0/modules/albumpassword/helpers/albumpassword_installer.php index 39b7ccee..93a6d0c0 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_installer.php @@ -30,10 +30,10 @@ class albumpassword_installer { // Create a table to store a list of all protected items in. $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( - `id` int(9) NOT NULL auto_increment, + `cache_id` int(9) NOT NULL auto_increment, `password_id` int(9) NOT NULL, `item_id` int(9) NOT NULL, - PRIMARY KEY (`id`)) + PRIMARY KEY (`cache_id`)) DEFAULT CHARSET=utf8;"); // Set the default value for this module's behavior. @@ -53,10 +53,10 @@ class albumpassword_installer { if ($version == 2) { // Create a table to store a list of all protected items in. $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( - `id` int(9) NOT NULL auto_increment, + `cache_id` int(9) NOT NULL auto_increment, `password_id` int(9) NOT NULL, `item_id` int(9) NOT NULL, - PRIMARY KEY (`id`)) + PRIMARY KEY (`cache_id`)) DEFAULT CHARSET=utf8;"); module::set_version("albumpassword", $version = 3); } diff --git a/3.1/modules/albumpassword/helpers/MY_access.php b/3.1/modules/albumpassword/helpers/MY_access.php index 38b48fc3..bda1db32 100644 --- a/3.1/modules/albumpassword/helpers/MY_access.php +++ b/3.1/modules/albumpassword/helpers/MY_access.php @@ -33,7 +33,7 @@ class access extends access_Core { // Throw a 404 error when a user attempts to access a protected item, // unless the password has been provided, or the user is the item's owner. } elseif (module::get_var("albumpassword", "hideonly") == false) { - $item_protected = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + $item_protected = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->order_by("cache_id")->find_all(); if (count($item_protected) > 0) { $existing_password = ORM::factory("items_albumpassword")->where("id", "=", $item_protected[0]->password_id)->find(); if ($existing_password->loaded()) { diff --git a/3.1/modules/albumpassword/helpers/albumpassword_installer.php b/3.1/modules/albumpassword/helpers/albumpassword_installer.php index 39b7ccee..93a6d0c0 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_installer.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_installer.php @@ -30,10 +30,10 @@ class albumpassword_installer { // Create a table to store a list of all protected items in. $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( - `id` int(9) NOT NULL auto_increment, + `cache_id` int(9) NOT NULL auto_increment, `password_id` int(9) NOT NULL, `item_id` int(9) NOT NULL, - PRIMARY KEY (`id`)) + PRIMARY KEY (`cache_id`)) DEFAULT CHARSET=utf8;"); // Set the default value for this module's behavior. @@ -53,10 +53,10 @@ class albumpassword_installer { if ($version == 2) { // Create a table to store a list of all protected items in. $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( - `id` int(9) NOT NULL auto_increment, + `cache_id` int(9) NOT NULL auto_increment, `password_id` int(9) NOT NULL, `item_id` int(9) NOT NULL, - PRIMARY KEY (`id`)) + PRIMARY KEY (`cache_id`)) DEFAULT CHARSET=utf8;"); module::set_version("albumpassword", $version = 3); } From adc6706ed91ae8cabfcdcb6b3b6b9658efd2052e Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 6 Feb 2011 21:22:11 -0700 Subject: [PATCH 271/300] Initial commit of Twitter module. OAuth access key retrieval and storage is working, posting tweets is not, yet. --- .../twitter/controllers/admin_twitter.php | 54 ++ 3.1/modules/twitter/css/twitter.css | 5 + 3.1/modules/twitter/helpers/twitter_event.php | 50 + 3.1/modules/twitter/helpers/twitter_theme.php | 25 + 3.1/modules/twitter/js/twitter.js | 36 + 3.1/modules/twitter/lib/OAuth.php | 874 ++++++++++++++++++ 3.1/modules/twitter/lib/images/darker.png | Bin 0 -> 2370 bytes 3.1/modules/twitter/lib/images/lighter.png | Bin 0 -> 2490 bytes 3.1/modules/twitter/lib/twitteroauth.php | 245 +++++ 3.1/modules/twitter/models/twitter_tweet.php | 21 + 3.1/modules/twitter/module.info | 3 + .../twitter/views/admin_twitter.html.php | 30 + 12 files changed, 1343 insertions(+) create mode 100644 3.1/modules/twitter/controllers/admin_twitter.php create mode 100644 3.1/modules/twitter/css/twitter.css create mode 100644 3.1/modules/twitter/helpers/twitter_event.php create mode 100644 3.1/modules/twitter/helpers/twitter_theme.php create mode 100644 3.1/modules/twitter/js/twitter.js create mode 100644 3.1/modules/twitter/lib/OAuth.php create mode 100644 3.1/modules/twitter/lib/images/darker.png create mode 100644 3.1/modules/twitter/lib/images/lighter.png create mode 100644 3.1/modules/twitter/lib/twitteroauth.php create mode 100644 3.1/modules/twitter/models/twitter_tweet.php create mode 100644 3.1/modules/twitter/module.info create mode 100644 3.1/modules/twitter/views/admin_twitter.html.php diff --git a/3.1/modules/twitter/controllers/admin_twitter.php b/3.1/modules/twitter/controllers/admin_twitter.php new file mode 100644 index 00000000..b2f61ab8 --- /dev/null +++ b/3.1/modules/twitter/controllers/admin_twitter.php @@ -0,0 +1,54 @@ +validate()) { + $consumer_key = $form->twitter_oauth->consumer_key->value; + $consumer_secret = $form->twitter_oauth->consumer_secret->value; + $default_tweet = $form->twitter_message->default_tweet->value; + $shorten_urls = $form->urls->shorten_urls->value; + + module::set_var("twitter", "consumer_key", $consumer_key); + module::set_var("twitter", "consumer_secret", $consumer_secret); + module::set_var("twitter", "default_tweet", $default_tweet); + module::set_var("twitter", "shorten_urls", $shorten_urls); + message::success("Twitter settings saved"); + } + } + $is_registered = twitter::is_registered(); + + $v = new Admin_View("admin.html"); + $v->page_title = t("Twitter"); + $v->content = new View("admin_twitter.html"); + $v->content->form = $form; + $v->content->is_registered = $is_registered; + + print $v; + } + +} \ No newline at end of file diff --git a/3.1/modules/twitter/css/twitter.css b/3.1/modules/twitter/css/twitter.css new file mode 100644 index 00000000..8bb82ccf --- /dev/null +++ b/3.1/modules/twitter/css/twitter.css @@ -0,0 +1,5 @@ +#g-twitter-character-count { + float: right; + font-size: 1.4em; + font-weight: bold; +} diff --git a/3.1/modules/twitter/helpers/twitter_event.php b/3.1/modules/twitter/helpers/twitter_event.php new file mode 100644 index 00000000..eabb047e --- /dev/null +++ b/3.1/modules/twitter/helpers/twitter_event.php @@ -0,0 +1,50 @@ +get("settings_menu") + ->append(Menu::factory("link") + ->id("twitter_menu") + ->label(t("Twitter")) + ->url(url::site("admin/twitter"))); + } + + static function site_menu($menu, $theme) { + $item = $theme->item(); + + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("twitter") + ->label(t("Share on Twitter")) + ->css_id("g-twitter-link") + ->url(url::site("twitter/dialog/{$item->id}"))); + } + + static function context_menu($menu, $theme, $item) { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("twitter") + ->label(t("Share on Twitter")) + ->css_class("ui-icon-link g-twitter-share") + ->url(url::site("twitter/dialog/{$item->id}"))); + } + +} diff --git a/3.1/modules/twitter/helpers/twitter_theme.php b/3.1/modules/twitter/helpers/twitter_theme.php new file mode 100644 index 00000000..2862966d --- /dev/null +++ b/3.1/modules/twitter/helpers/twitter_theme.php @@ -0,0 +1,25 @@ +script("twitter.js"); + $theme->css("twitter.css"); + } +} diff --git a/3.1/modules/twitter/js/twitter.js b/3.1/modules/twitter/js/twitter.js new file mode 100644 index 00000000..3a816a9b --- /dev/null +++ b/3.1/modules/twitter/js/twitter.js @@ -0,0 +1,36 @@ +(function($) { + $.widget("ui.gallery_twitter", { + + _init: function() { + this._set_count(); + $(this.element).bind("keyup", this._set_count); + }, + + _set_count: function() { + var character_array = $("#g-tweet").val().split(""); + var count = character_array.length; + var remaining = 140 - count; //self.options.max_count - count; + var count_container = $("#g-twitter-character-count"); + var color = "#000000"; + var warn_color = "#7F0005"; //this.options.warn_color; + var error_color = "#FF0000"; //this.options.error_color; + if (remaining < 10) { + color = error_color; + } else if (remaining < 20) { + color = warn_color; + } + $(count_container).css("color", color); + $(count_container).html(remaining); + } + + }); + + $.extend($.ui.gallery_twitter, { + defaults: { + max_count: 140, + warn_color: "#7F0005", + error_color: "#FF0000" + } + }); + +})(jQuery); diff --git a/3.1/modules/twitter/lib/OAuth.php b/3.1/modules/twitter/lib/OAuth.php new file mode 100644 index 00000000..67a94c47 --- /dev/null +++ b/3.1/modules/twitter/lib/OAuth.php @@ -0,0 +1,874 @@ +key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + } + + function __toString() { + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + } +} + +class OAuthToken { + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) { + $this->key = $key; + $this->secret = $secret; + } + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() { + return "oauth_token=" . + OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . + OAuthUtil::urlencode_rfc3986($this->secret); + } + + function __toString() { + return $this->to_string(); + } +} + +/** + * A class for implementing a Signature Method + * See section 9 ("Signing Requests") in the spec + */ +abstract class OAuthSignatureMethod { + /** + * Needs to return the name of the Signature Method (ie HMAC-SHA1) + * @return string + */ + abstract public function get_name(); + + /** + * Build up the signature + * NOTE: The output of this function MUST NOT be urlencoded. + * the encoding is handled in OAuthRequest when the final + * request is serialized + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @return string + */ + abstract public function build_signature($request, $consumer, $token); + + /** + * Verifies that a given signature is correct + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @param string $signature + * @return bool + */ + public function check_signature($request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +} + +/** + * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] + * where the Signature Base String is the text and the key is the concatenated values (each first + * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' + * character (ASCII code 38) even if empty. + * - Chapter 9.2 ("HMAC-SHA1") + */ +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { + function get_name() { + return "HMAC-SHA1"; + } + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + + return base64_encode(hash_hmac('sha1', $base_string, $key, true)); + } +} + +/** + * The PLAINTEXT method does not provide any security protection and SHOULD only be used + * over a secure channel such as HTTPS. It does not use the Signature Base String. + * - Chapter 9.4 ("PLAINTEXT") + */ +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { + public function get_name() { + return "PLAINTEXT"; + } + + /** + * oauth_signature is set to the concatenated encoded values of the Consumer Secret and + * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is + * empty. The result MUST be encoded again. + * - Chapter 9.4.1 ("Generating Signatures") + * + * Please note that the second encoding MUST NOT happen in the SignatureMethod, as + * OAuthRequest handles this! + */ + public function build_signature($request, $consumer, $token) { + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + $request->base_string = $key; + + return $key; + } +} + +/** + * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in + * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for + * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a + * verified way to the Service Provider, in a manner which is beyond the scope of this + * specification. + * - Chapter 9.3 ("RSA-SHA1") + */ +abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { + public function get_name() { + return "RSA-SHA1"; + } + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // Either way should return a string representation of the certificate + protected abstract function fetch_public_cert(&$request); + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // Either way should return a string representation of the certificate + protected abstract function fetch_private_cert(&$request); + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } + + public function check_signature($request, $consumer, $token, $signature) { + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } +} + +class OAuthRequest { + private $parameters; + private $http_method; + private $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + public static $POST_INPUT = 'php://input'; + + function __construct($http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + } + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") + ? 'http' + : 'https'; + @$http_url or $http_url = $scheme . + '://' . $_SERVER['HTTP_HOST'] . + ':' . + $_SERVER['SERVER_PORT'] . + $_SERVER['REQUEST_URI']; + @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; + + // We weren't handed any parameters, so let's find the ones relevant to + // this request. + // If you run XML-RPC or similar you should use this to provide your own + // parsed parameter-list + if (!$parameters) { + // Find request headers + $request_headers = OAuthUtil::get_headers(); + + // Parse the query-string to find GET parameters + $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); + + // It's a POST request of the proper content-type, so parse POST + // parameters and add those overriding any duplicates from GET + if ($http_method == "POST" + && @strstr($request_headers["Content-Type"], + "application/x-www-form-urlencoded") + ) { + $post_data = OAuthUtil::parse_parameters( + file_get_contents(self::$POST_INPUT) + ); + $parameters = array_merge($parameters, $post_data); + } + + // We have a Authorization-header with OAuth data. Parse the header + // and add those overriding any duplicates from GET or POST + if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { + $header_parameters = OAuthUtil::split_header( + $request_headers['Authorization'] + ); + $parameters = array_merge($parameters, $header_parameters); + } + + } + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + if ($token) + $defaults['oauth_token'] = $token->key; + + $parameters = array_merge($defaults, $parameters); + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + public function set_parameter($name, $value, $allow_duplicates = true) { + if ($allow_duplicates && isset($this->parameters[$name])) { + // We have already added parameter(s) with this name, so add to the list + if (is_scalar($this->parameters[$name])) { + // This is the first duplicate, so transform scalar (string) + // into an array so we can add the duplicates + $this->parameters[$name] = array($this->parameters[$name]); + } + + $this->parameters[$name][] = $value; + } else { + $this->parameters[$name] = $value; + } + } + + public function get_parameter($name) { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + public function get_parameters() { + return $this->parameters; + } + + public function unset_parameter($name) { + unset($this->parameters[$name]); + } + + /** + * The request parameters, sorted and concatenated into a normalized string. + * @return string + */ + public function get_signable_parameters() { + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + return OAuthUtil::build_http_query($params); + } + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() { + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + } + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() { + return strtoupper($this->http_method); + } + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $port = @$parts['port']; + $scheme = $parts['scheme']; + $host = $parts['host']; + $path = @$parts['path']; + + $port or $port = ($scheme == 'https') ? '443' : '80'; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() { + $post_data = $this->to_postdata(); + $out = $this->get_normalized_http_url(); + if ($post_data) { + $out .= '?'.$post_data; + } + return $out; + } + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() { + return OAuthUtil::build_http_query($this->parameters); + } + + /** + * builds the Authorization: header + */ + public function to_header($realm=null) { + $first = true; + if($realm) { + $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; + $first = false; + } else + $out = 'Authorization: OAuth'; + + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) { + throw new OAuthException('Arrays not supported in headers'); + } + $out .= ($first) ? ' ' : ','; + $out .= OAuthUtil::urlencode_rfc3986($k) . + '="' . + OAuthUtil::urlencode_rfc3986($v) . + '"'; + $first = false; + } + return $out; + } + + public function __toString() { + return $this->to_url(); + } + + + public function sign_request($signature_method, $consumer, $token) { + $this->set_parameter( + "oauth_signature_method", + $signature_method->get_name(), + false + ); + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature, false); + } + + public function build_signature($signature_method, $consumer, $token) { + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + } + + /** + * util function: current timestamp + */ + private static function generate_timestamp() { + return time(); + } + + /** + * util function: current nonce + */ + private static function generate_nonce() { + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + } +} + +class OAuthServer { + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = '1.0'; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) { + $this->data_store = $data_store; + } + + public function add_signature_method($signature_method) { + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + } + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $callback = $request->get_parameter('oauth_callback'); + $new_token = $this->data_store->new_request_token($consumer, $callback); + + return $new_token; + } + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $verifier = $request->get_parameter('oauth_verifier'); + $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); + + return $new_token; + } + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + } + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) { + $version = $request->get_parameter("oauth_version"); + if (!$version) { + // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. + // Chapter 7.0 ("Accessing Protected Ressources") + $version = '1.0'; + } + if ($version !== $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + /** + * figure out the signature with some defaults + */ + private function get_signature_method(&$request) { + $signature_method = + @$request->get_parameter("oauth_signature_method"); + + if (!$signature_method) { + // According to chapter 7 ("Accessing Protected Ressources") the signature-method + // parameter is required, and we can't just fallback to PLAINTEXT + throw new OAuthException('No signature method parameter. This parameter is required'); + } + + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported " . + "try one of the following: " . + implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + } + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer(&$request) { + $consumer_key = @$request->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + } + + /** + * try to find the token for the provided request's token key + */ + private function get_token(&$request, $consumer, $token_type="access") { + $token_field = @$request->get_parameter('oauth_token'); + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature(&$request, $consumer, $token) { + // this should probably be in a different method + $timestamp = @$request->get_parameter('oauth_timestamp'); + $nonce = @$request->get_parameter('oauth_nonce'); + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) { + if( ! $timestamp ) + throw new OAuthException( + 'Missing timestamp parameter. The parameter is required' + ); + + // verify that timestamp is recentish + $now = time(); + if (abs($now - $timestamp) > $this->timestamp_threshold) { + throw new OAuthException( + "Expired timestamp, yours $timestamp, ours $now" + ); + } + } + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) { + if( ! $nonce ) + throw new OAuthException( + 'Missing nonce parameter. The parameter is required' + ); + + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce( + $consumer, + $token, + $nonce, + $timestamp + ); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + } + +} + +class OAuthDataStore { + function lookup_consumer($consumer_key) { + // implement me + } + + function lookup_token($consumer, $token_type, $token) { + // implement me + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + // implement me + } + + function new_request_token($consumer, $callback = null) { + // return a new token attached to this consumer + } + + function new_access_token($token, $consumer, $verifier = null) { + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + } + +} + +class OAuthUtil { + public static function urlencode_rfc3986($input) { + if (is_array($input)) { + return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace( + '+', + ' ', + str_replace('%7E', '~', rawurlencode($input)) + ); + } else { + return ''; + } +} + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) { + return urldecode($string); + } + + // Utility function for turning the Authorization: header into + // parameters, has to do some unescaping + // Can filter out any non-oauth parameters if needed (default behaviour) + public static function split_header($header, $only_allow_oauth_parameters = true) { + $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; + $offset = 0; + $params = array(); + while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + $match = $matches[0]; + $header_name = $matches[2][0]; + $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; + if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { + $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); + } + $offset = $match[1] + strlen($match[0]); + } + + if (isset($params['realm'])) { + unset($params['realm']); + } + + return $params; + } + + // helper to try to sort out headers for people who aren't running apache + public static function get_headers() { + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + $headers = apache_request_headers(); + + // sanitize the output of apache_request_headers because + // we always want the keys to be Cased-Like-This and arh() + // returns the headers in the same case as they are in the + // request + $out = array(); + foreach( $headers AS $key => $value ) { + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("-", " ", $key))) + ); + $out[$key] = $value; + } + } else { + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + if( isset($_SERVER['CONTENT_TYPE']) ) + $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; + if( isset($_ENV['CONTENT_TYPE']) ) + $out['Content-Type'] = $_ENV['CONTENT_TYPE']; + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) + ); + $out[$key] = $value; + } + } + } + return $out; + } + + // This function takes a input like a=b&a=c&d=e and returns the parsed + // parameters like this + // array('a' => array('b','c'), 'd' => 'e') + public static function parse_parameters( $input ) { + if (!isset($input) || !$input) return array(); + + $pairs = explode('&', $input); + + $parsed_parameters = array(); + foreach ($pairs as $pair) { + $split = explode('=', $pair, 2); + $parameter = OAuthUtil::urldecode_rfc3986($split[0]); + $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; + + if (isset($parsed_parameters[$parameter])) { + // We have already recieved parameter(s) with this name, so add to the list + // of parameters with this name + + if (is_scalar($parsed_parameters[$parameter])) { + // This is the first duplicate, so transform scalar (string) into an array + // so we can add the duplicates + $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); + } + + $parsed_parameters[$parameter][] = $value; + } else { + $parsed_parameters[$parameter] = $value; + } + } + return $parsed_parameters; + } + + public static function build_http_query($params) { + if (!$params) return ''; + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($params, 'strcmp'); + + $pairs = array(); + foreach ($params as $parameter => $value) { + if (is_array($value)) { + // If two or more parameters share the same name, they are sorted by their value + // Ref: Spec: 9.1.1 (1) + natsort($value); + foreach ($value as $duplicate_value) { + $pairs[] = $parameter . '=' . $duplicate_value; + } + } else { + $pairs[] = $parameter . '=' . $value; + } + } + // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) + // Each name-value pair is separated by an '&' character (ASCII code 38) + return implode('&', $pairs); + } +} + +?> diff --git a/3.1/modules/twitter/lib/images/darker.png b/3.1/modules/twitter/lib/images/darker.png new file mode 100644 index 0000000000000000000000000000000000000000..746b6b9f80c71049f2a4eb84ff72d5d1bf77c62c GIT binary patch literal 2370 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 literal 0 HcmV?d00001 diff --git a/3.1/modules/twitter/lib/images/lighter.png b/3.1/modules/twitter/lib/images/lighter.png new file mode 100644 index 0000000000000000000000000000000000000000..297bb03404f2d7462ee9355aae38f5f5f3e47fbd GIT binary patch literal 2490 zcmV;r2}SmaP)dbVG7wVRUJ4ZXi@?ZDjy8FEKeU zFgbGL({lg-0338hSaefwW^{L9a%BKPWN%_+AVz6&Wp{6KYjYq&Q#1y$)1UwV2@^>~ zK~!jg?VEdWl+_u=f8X9WA)72QVM!vF1VSK4AZQ?nK}o}NseS3JN$f zgRQMIo!W~OJ6`ZouvAJ_K&eH%O=zU35Dd9+OGJ}EvYTu++57IdU;kJVc9XpY6B?a; zX3k{ZIp=-eC+|7m`<-)kR{+>vQ7sWi9D|5ebEgWz_KIq$s4!>9f*d_|j-lj_V|e1| zv2!Q@e7>PCTvsDT9K#C=07>!<2 zJRT3v{^4m(e0B`AMuXes=AQD^+;C$tPj22q>4H*zdfz>`#KAid3>9xAeo+*W%jE#P zv+pgwyLbU*^X6iX@D|vxdJPV{BV^y=rMIzo>FtA}CzL%H7|@3PC-uR0k{o`MEpT&+ zmiCUwd$DjaD&G5mR^R=5Ugy>A zFL6`pd;osE=`rd~)-ZEc5shchvh}x5aZ~9nJpS;;UjCggyhMFX4IM@k^;K2OnpeX9 zJ+G59W-M>Ndx+<@J`*QB2?^MN1N-(fead7?=HEi6T!B@Box5IR=L;`>Ej{PX zHuTf??pp_V_K#2V&%f=WuDXUP)2>6K(eP;bS}ruV@b7*5sr;ydIrB8I2tIsDgsYdEB~aA*X6k7? zxX|2;qp^|Gwe@`Y#d&IupCC)EqUw`l7)GU&uTKu*U$o?QG#U*z%qZYj8`hDRoCLu8 zhbzgOI5DbLyT^;&?d{E@=<{Loc(HlBWQ@7?YwIarxgyB6oH@guS9eiXI-mWozJlq( z1uEY@fC2%N(MZ*iqs%Lw&3$(-XHs%f#Ci8GF3A_N^~J`q@N(Yec4#`>p8;Rm>`^(w z^()mX);zY6A1}KTOQ(hBpZ+tI2M_vvC6EgOasg^TuSW9u`2L~=xMTvlF=IdirA&q( z01}jz+(`M#6-+Id=Jypvl2wZSngV!x>{xvBuA7QRt)c4kYU--0D3~!Fhr>aA^+`_G z)iHDWG@POs#IMsQgCL+*sYp&si#>KieJ zAZPaXZl!3>9M-SjNX7dfvh?=bdf)$&1YWO*1jqy#ZFVPakM~lt{ns24pi(Mnce*e+ zoV|U5)IWA0uWtapa=C)>xf3{EU4zZ(WX19y(%RC@hySVMd_x1@*}REPr@KGD1PCBr zszd|P3jULJU(P|*6JUEpf1ZUYMN@b5y{gO~R0`R^ zzo2^1b1iYS>vBugsL9qPM&yVTyCwWp(Uo=ev@y*W7rS{ZmOePbh#j}_&X%YtxeZa(=9L5<&(e8Bi z<5wsYm^&^0MKr*8(egMLdIIf9`S}2Jn9Y=xl~T570XoAdF4!DlHH6a}anEJ-cv*VQ z<;7KRgYq-}Q)5qi@;qcECS38JtLDV#Y+3g(n;(3LUp}&dB%O}ct5)I-xFV2%>k11g zTUf@C&yKTa*KVdw^(R50loR*{_b2SKCHeZt4jkLpj^;ZlKOe6sQZ!>aT9uM(b0*;N zdYL_=0IS=B(gj7|CTgIo3w=s5DLNgUE;lDl z=6*ck(9~r1FRqNt5);TzP2p%4ylH~wfExv!+`pk8s}?!j(i+mgrP)GJVGd2L zrifxR3OQ=I9MLCX4Ray;#3LJ#2{HtkfT^vG#S0hk#JUIhwAC2&WDQrS)2nfk*V=iQ!Y2K(sVlqHpM%x^1{ck;y4Kin5jc`W;@8<8AFx4+YWs z?QSo2A}4+Q<6F?#y0F{rhttp_status; } + function lastAPICall() { return $this->last_api_call; } + + /** + * construct TwitterOAuth object + */ + function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { + $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); + if (!empty($oauth_token) && !empty($oauth_token_secret)) { + $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); + } else { + $this->token = NULL; + } + } + + + /** + * Get a request_token from Twitter + * + * @returns a key/value array containing oauth_token and oauth_token_secret + */ + function getRequestToken($oauth_callback = NULL) { + $parameters = array(); + if (!empty($oauth_callback)) { + $parameters['oauth_callback'] = $oauth_callback; + } + $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * Get the authorize URL + * + * @returns a string + */ + function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) { + if (is_array($token)) { + $token = $token['oauth_token']; + } + if (empty($sign_in_with_twitter)) { + return $this->authorizeURL() . "?oauth_token={$token}"; + } else { + return $this->authenticateURL() . "?oauth_token={$token}"; + } + } + + /** + * Exchange request token and secret for an access token and + * secret, to sign API calls. + * + * @returns array("oauth_token" => "the-access-token", + * "oauth_token_secret" => "the-access-secret", + * "user_id" => "9436992", + * "screen_name" => "abraham") + */ + function getAccessToken($oauth_verifier = FALSE) { + $parameters = array(); + if (!empty($oauth_verifier)) { + $parameters['oauth_verifier'] = $oauth_verifier; + } + $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * One time exchange of username and password for access token and secret. + * + * @returns array("oauth_token" => "the-access-token", + * "oauth_token_secret" => "the-access-secret", + * "user_id" => "9436992", + * "screen_name" => "abraham", + * "x_auth_expires" => "0") + */ + function getXAuthToken($username, $password) { + $parameters = array(); + $parameters['x_auth_username'] = $username; + $parameters['x_auth_password'] = $password; + $parameters['x_auth_mode'] = 'client_auth'; + $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * GET wrapper for oAuthRequest. + */ + function get($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'GET', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * POST wrapper for oAuthRequest. + */ + function post($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'POST', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * DELETE wrapper for oAuthReqeust. + */ + function delete($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'DELETE', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * Format and sign an OAuth / API request + */ + function oAuthRequest($url, $method, $parameters) { + if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { + $url = "{$this->host}{$url}.{$this->format}"; + } + $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); + $request->sign_request($this->sha1_method, $this->consumer, $this->token); + switch ($method) { + case 'GET': + return $this->http($request->to_url(), 'GET'); + default: + return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); + } + } + + /** + * Make an HTTP request + * + * @return API results + */ + function http($url, $method, $postfields = NULL) { + $this->http_info = array(); + $ci = curl_init(); + /* Curl settings */ + curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); + curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); + curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:')); + curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); + curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); + curl_setopt($ci, CURLOPT_HEADER, FALSE); + + switch ($method) { + case 'POST': + curl_setopt($ci, CURLOPT_POST, TRUE); + if (!empty($postfields)) { + curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); + } + break; + case 'DELETE': + curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); + if (!empty($postfields)) { + $url = "{$url}?{$postfields}"; + } + } + + curl_setopt($ci, CURLOPT_URL, $url); + $response = curl_exec($ci); + $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); + $this->url = $url; + curl_close ($ci); + return $response; + } + + /** + * Get the header info to store. + */ + function getHeader($ch, $header) { + $i = strpos($header, ':'); + if (!empty($i)) { + $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); + $value = trim(substr($header, $i + 2)); + $this->http_header[$key] = $value; + } + return strlen($header); + } +} diff --git a/3.1/modules/twitter/models/twitter_tweet.php b/3.1/modules/twitter/models/twitter_tweet.php new file mode 100644 index 00000000..9a616cfd --- /dev/null +++ b/3.1/modules/twitter/models/twitter_tweet.php @@ -0,0 +1,21 @@ + +
    +

    + +

    dev.twitter.com/apps/new.", + array("twitter_apps_reg" => "http://dev.twitter.com/apps/new")) ?>

    +
      +
    • +
    • +
    • url::abs_site())) ?>
    • +
    • +
    • +
    • url::abs_site())) ?>
    • +
    • +
    +

    + +

    Twitter application settings.", + array("twitter_apps" => "http://dev.twitter.com/apps")) ?>

    + +

    bit.ly module to shorten + Gallery URLs in tweets.", array("bitly_module_url" => "http://codex.gallery2.org/Gallery3:Modules:bitly")) ?>

    + + +
    + +
    +
    From 265901c3d538907060ad777fa9f7170a08c191d1 Mon Sep 17 00:00:00 2001 From: rWatcher Date: Tue, 8 Feb 2011 15:34:12 -0500 Subject: [PATCH 272/300] Minor bugfix. --- 3.0/modules/albumpassword/helpers/albumpassword_event.php | 6 +++--- 3.1/modules/albumpassword/helpers/albumpassword_event.php | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/3.0/modules/albumpassword/helpers/albumpassword_event.php b/3.0/modules/albumpassword/helpers/albumpassword_event.php index cd981af1..0c9210ea 100644 --- a/3.0/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.0/modules/albumpassword/helpers/albumpassword_event.php @@ -86,7 +86,7 @@ class albumpassword_event_Core { ->where("albumpassword_idcaches.item_id", "IS NOT", NULL)->close() ->descendants(); - $existing_cacheditem = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + $existing_cacheditem = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->order_by("cache_id")->find_all(); if ((count($existing_cacheditem) == 0) && count($passworded_subitems) == 0) { $menu->get("options_menu") ->append(Menu::factory("dialog") @@ -116,7 +116,7 @@ class albumpassword_event_Core { static function item_created($item) { // Check for any already existing password on parent album(s), if found, generate cache data for the new item. - $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->order_by("cache_id")->find_all(); if (count($existing_password) > 0) { $new_cachedid = ORM::factory("albumpassword_idcache"); $new_cachedid->password_id = $existing_password[0]->password_id; @@ -130,7 +130,7 @@ class albumpassword_event_Core { db::build()->delete("albumpassword_idcaches")->where("item_id", "=", $item->id)->execute(); // Check for a password on the new parent, generate cache data if necessary. - $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->order_by("cache_id")->find_all(); if (count($existing_password) > 0) { $new_cachedid = ORM::factory("albumpassword_idcache"); $new_cachedid->password_id = $existing_password[0]->password_id; diff --git a/3.1/modules/albumpassword/helpers/albumpassword_event.php b/3.1/modules/albumpassword/helpers/albumpassword_event.php index cd981af1..0c9210ea 100644 --- a/3.1/modules/albumpassword/helpers/albumpassword_event.php +++ b/3.1/modules/albumpassword/helpers/albumpassword_event.php @@ -86,7 +86,7 @@ class albumpassword_event_Core { ->where("albumpassword_idcaches.item_id", "IS NOT", NULL)->close() ->descendants(); - $existing_cacheditem = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->find_all(); + $existing_cacheditem = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->id)->order_by("cache_id")->find_all(); if ((count($existing_cacheditem) == 0) && count($passworded_subitems) == 0) { $menu->get("options_menu") ->append(Menu::factory("dialog") @@ -116,7 +116,7 @@ class albumpassword_event_Core { static function item_created($item) { // Check for any already existing password on parent album(s), if found, generate cache data for the new item. - $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->order_by("cache_id")->find_all(); if (count($existing_password) > 0) { $new_cachedid = ORM::factory("albumpassword_idcache"); $new_cachedid->password_id = $existing_password[0]->password_id; @@ -130,7 +130,7 @@ class albumpassword_event_Core { db::build()->delete("albumpassword_idcaches")->where("item_id", "=", $item->id)->execute(); // Check for a password on the new parent, generate cache data if necessary. - $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->find_all(); + $existing_password = ORM::factory("albumpassword_idcache")->where("item_id", "=", $item->parent_id)->order_by("cache_id")->find_all(); if (count($existing_password) > 0) { $new_cachedid = ORM::factory("albumpassword_idcache"); $new_cachedid->password_id = $existing_password[0]->password_id; From 782f8372fba26f81d8a4e0b05e39756a7c022a0c Mon Sep 17 00:00:00 2001 From: rWatcher Date: Tue, 8 Feb 2011 15:49:52 -0500 Subject: [PATCH 273/300] Created a maintance task for upgrading from older versions of AlbumPassword. --- .../helpers/albumpassword_task.php | 138 ++++++++++++++++++ .../helpers/albumpassword_task.php | 138 ++++++++++++++++++ 2 files changed, 276 insertions(+) create mode 100644 3.0/modules/albumpassword/helpers/albumpassword_task.php create mode 100644 3.1/modules/albumpassword/helpers/albumpassword_task.php diff --git a/3.0/modules/albumpassword/helpers/albumpassword_task.php b/3.0/modules/albumpassword/helpers/albumpassword_task.php new file mode 100644 index 00000000..b6ea007a --- /dev/null +++ b/3.0/modules/albumpassword/helpers/albumpassword_task.php @@ -0,0 +1,138 @@ +join("albumpassword_idcaches", "items_albumpasswords.id", "albumpassword_idcaches.password_id", "LEFT OUTER") + ->and_where("albumpassword_idcaches.password_id", "IS", NULL)->count_all(); + + return array(Task_Definition::factory() + ->callback("albumpassword_task::update_idcaches") + ->name(t("Rebuild Album Password ID Caches DB")) + ->description(t("Logs the contents of all protected albums into the db.")) + ->severity($bad_albums ? log::WARNING : log::SUCCESS)); + } + + static function update_idcaches($task) { + // Populate the idcaches table with the contents of all protected albums. + + $start = microtime(true); + $total = $task->get("total"); + $existing_passwords = ORM::factory("items_albumpassword")->find_all(); + // If this is the first time this function has been run, + // delete and re-create the idcaches table, and set up + // some initial variables. + if (empty($total)) { + // Delete the idcache table and make a new one. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {albumpassword_idcaches};"); + $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( + `cache_id` int(9) NOT NULL auto_increment, + `password_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`cache_id`)) + DEFAULT CHARSET=utf8;"); + + // Set the initial values for all variables. + $task->set("total", count($existing_passwords)); + $total = $task->get("total"); + $task->set("last_album_counter", 0); + $task->set("last_id", 0); + $task->set("completed_albums", 0); + $task->set("completed_items", 0); + $task->set("total_items", 0); + } + + // Retrieve the values for variables from the last time this + // function was run. + $last_album_counter = $task->get("last_album_counter"); + $completed_albums = $task->get("completed_albums"); + $completed_items = $task->get("completed_items"); + $total_items = $task->get("total_items"); + $last_id = $task->get("last_id"); + + // If completed_items is 0, then we're just starting to process this + // album. Add the album to idcaches before adding it's contents. + if ($completed_items == 0) { + // Add the album to the id cache. + $cached_album = ORM::factory("albumpassword_idcache"); + $cached_album->password_id = $existing_passwords[$last_album_counter]->id; + $cached_album->item_id = $existing_passwords[$last_album_counter]->album_id; + $cached_album->save(); + + // Set total_items to the number of items in this album. + $total_items = ORM::factory("item", $existing_passwords[$last_album_counter]->album_id) + ->descendants_count(); + $task->set("total_items", $total_items); + } + + // Add each item in the album to idcaches. + foreach (ORM::factory("item", $existing_passwords[$last_album_counter]->album_id) + ->where("id", ">", $last_id) + ->order_by("id") + ->descendants(100) as $item) { + + $cached_item = ORM::factory("albumpassword_idcache"); + $cached_item->password_id =$existing_passwords[$last_album_counter]->id; + $cached_item->item_id = $item->id; + $cached_item->save(); + + $last_id = $item->id; + $completed_items++; + + // Set a time limit so the script doesn't time out. + if (microtime(true) - $start > 1.5) { + break; + } + } // end foreach + + // If completed_items equals total_items, then we've + // processed everything in the current album. + // Increase variables and set everything up for the + // next album. + if ($completed_items == $total_items) { + $completed_items = 0; + $last_album_counter++; + $completed_albums++; + $last_id = 0; + } + + // Store the current values of the variables for the next + // time this function is called. + $task->set("last_album_counter", $last_album_counter); + $task->set("last_id", $last_id); + $task->set("completed_albums", $completed_albums); + $task->set("completed_items", $completed_items); + + // Display the number of albums that have been completed before exiting. + if ($total == $completed_albums) { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + $task->status = t("Scanning Protected Album $completed_albums of $total"); + } else { + $task->percent_complete = round(100 * $completed / $total); + $task->status = t("Scanning Protected Album $completed_albums of $total -- $completed_items / $total_items files"); + } + } +} diff --git a/3.1/modules/albumpassword/helpers/albumpassword_task.php b/3.1/modules/albumpassword/helpers/albumpassword_task.php new file mode 100644 index 00000000..b6ea007a --- /dev/null +++ b/3.1/modules/albumpassword/helpers/albumpassword_task.php @@ -0,0 +1,138 @@ +join("albumpassword_idcaches", "items_albumpasswords.id", "albumpassword_idcaches.password_id", "LEFT OUTER") + ->and_where("albumpassword_idcaches.password_id", "IS", NULL)->count_all(); + + return array(Task_Definition::factory() + ->callback("albumpassword_task::update_idcaches") + ->name(t("Rebuild Album Password ID Caches DB")) + ->description(t("Logs the contents of all protected albums into the db.")) + ->severity($bad_albums ? log::WARNING : log::SUCCESS)); + } + + static function update_idcaches($task) { + // Populate the idcaches table with the contents of all protected albums. + + $start = microtime(true); + $total = $task->get("total"); + $existing_passwords = ORM::factory("items_albumpassword")->find_all(); + // If this is the first time this function has been run, + // delete and re-create the idcaches table, and set up + // some initial variables. + if (empty($total)) { + // Delete the idcache table and make a new one. + $db = Database::instance(); + $db->query("DROP TABLE IF EXISTS {albumpassword_idcaches};"); + $db->query("CREATE TABLE IF NOT EXISTS {albumpassword_idcaches} ( + `cache_id` int(9) NOT NULL auto_increment, + `password_id` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + PRIMARY KEY (`cache_id`)) + DEFAULT CHARSET=utf8;"); + + // Set the initial values for all variables. + $task->set("total", count($existing_passwords)); + $total = $task->get("total"); + $task->set("last_album_counter", 0); + $task->set("last_id", 0); + $task->set("completed_albums", 0); + $task->set("completed_items", 0); + $task->set("total_items", 0); + } + + // Retrieve the values for variables from the last time this + // function was run. + $last_album_counter = $task->get("last_album_counter"); + $completed_albums = $task->get("completed_albums"); + $completed_items = $task->get("completed_items"); + $total_items = $task->get("total_items"); + $last_id = $task->get("last_id"); + + // If completed_items is 0, then we're just starting to process this + // album. Add the album to idcaches before adding it's contents. + if ($completed_items == 0) { + // Add the album to the id cache. + $cached_album = ORM::factory("albumpassword_idcache"); + $cached_album->password_id = $existing_passwords[$last_album_counter]->id; + $cached_album->item_id = $existing_passwords[$last_album_counter]->album_id; + $cached_album->save(); + + // Set total_items to the number of items in this album. + $total_items = ORM::factory("item", $existing_passwords[$last_album_counter]->album_id) + ->descendants_count(); + $task->set("total_items", $total_items); + } + + // Add each item in the album to idcaches. + foreach (ORM::factory("item", $existing_passwords[$last_album_counter]->album_id) + ->where("id", ">", $last_id) + ->order_by("id") + ->descendants(100) as $item) { + + $cached_item = ORM::factory("albumpassword_idcache"); + $cached_item->password_id =$existing_passwords[$last_album_counter]->id; + $cached_item->item_id = $item->id; + $cached_item->save(); + + $last_id = $item->id; + $completed_items++; + + // Set a time limit so the script doesn't time out. + if (microtime(true) - $start > 1.5) { + break; + } + } // end foreach + + // If completed_items equals total_items, then we've + // processed everything in the current album. + // Increase variables and set everything up for the + // next album. + if ($completed_items == $total_items) { + $completed_items = 0; + $last_album_counter++; + $completed_albums++; + $last_id = 0; + } + + // Store the current values of the variables for the next + // time this function is called. + $task->set("last_album_counter", $last_album_counter); + $task->set("last_id", $last_id); + $task->set("completed_albums", $completed_albums); + $task->set("completed_items", $completed_items); + + // Display the number of albums that have been completed before exiting. + if ($total == $completed_albums) { + $task->done = true; + $task->state = "success"; + $task->percent_complete = 100; + $task->status = t("Scanning Protected Album $completed_albums of $total"); + } else { + $task->percent_complete = round(100 * $completed / $total); + $task->status = t("Scanning Protected Album $completed_albums of $total -- $completed_items / $total_items files"); + } + } +} From e9235ba4c410dce339e362578d450db8d4785023 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 9 Feb 2011 18:40:32 -0700 Subject: [PATCH 274/300] Initial commit of Twitter module. OAuth access key retrieval and storage is working, posting tweets is not, yet. --- 3.1/modules/twitter/controllers/twitter.php | 235 ++++++++++++++++++ 3.1/modules/twitter/helpers/twitter.php | 122 +++++++++ .../twitter/helpers/twitter_installer.php | 50 ++++ 3 files changed, 407 insertions(+) create mode 100644 3.1/modules/twitter/controllers/twitter.php create mode 100644 3.1/modules/twitter/helpers/twitter.php create mode 100644 3.1/modules/twitter/helpers/twitter_installer.php diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php new file mode 100644 index 00000000..e55ed674 --- /dev/null +++ b/3.1/modules/twitter/controllers/twitter.php @@ -0,0 +1,235 @@ +id; + $token_is_set = $this->_is_token_set($user_id); + + $v = new View("twitter_dialog.html"); + $v->is_registered = twitter::is_registered(); + $v->user_token_set = $token_is_set; + + if ($token_is_set) { + $v->type = $item->type; + $v->title = $item->title; + $v->description = $item->description; + $v->form = $form; + $v->character_count = twitter::$character_count; + } else { + $item_url = urlencode(url::abs_site($item->relative_url_cache)); + $v->user_id = $user_id; + $v->twitter_auth_url = url::site("twitter/redirect?item_url=$item_url"); + } + + print $v; + } + + /** + * Check if current user's Twitter credentials have been stored locally. + * @param int $user_id + * @return boolean + */ + private function _is_token_set($user_id) { + $twitter_user = $this->_get_twitter_user($user_id); + if (!empty($twitter_user->oauth_token) && !empty($twitter_user->oauth_token_secret)) { + return true; + } + return false; + } + + /** + * Get Twitter credentials for the current user. + * @param int $user_id + * @return mixed object|false + */ + private function _get_twitter_user($user_id) { + $twitter_user = ORM::factory("twitter_user")->where("user_id", "=", $user_id)->find(); + if ($twitter_user->loaded()) { + return $twitter_user; + } + return false; + } + + /** + * Verify credentials and redirect based on response from Twitter. + */ + public function callback() { + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + $oauth_token = Session::instance()->get("twitter_oauth_token"); + $oauth_token_secret = Session::instance()->get("twitter_oauth_token_secret"); + $item_url = Session::instance()->get("twitter_item_redirect"); + + // If the oauth_token is old redirect to the connect page + if (isset($_REQUEST['oauth_token']) && $oauth_token !== $_REQUEST['oauth_token']) { + Session::instance()->set("twitter_oauth_status", "old_token"); + $this->clear_twitter_session(); + url::redirect(url::site("twitter/redirect")); + } + + // Create TwitteroAuth object with app key/secret and token key/secret from default phase + $connection = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); + + // Request access tokens from twitter + $access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']); + + // Save the access tokens + Session::instance()->set("twitter_access_token", $access_token); + + // Remove no longer needed request tokens + Session::instance()->delete("twitter_oauth_token"); + Session::instance()->delete("twitter_oauth_token_secret"); + + // If HTTP response is 200 continue otherwise send to connect page to retry + if (200 == $connection->http_code) { + // The user has been verified and the access tokens can be saved for future use + $this->save_twitter_user($access_token); + // Redirect to the tweet form + $item = ORM::factory("item", $item_id); + url::redirect(url::abs_site($item_url)); + } else { + // @todo Log HTTP status for application log and/or error message + $this->clear_twitter_session(); + url::redirect(url::site("twitter/redirect")); + } + } + + /** + * Save or update the current user's Twitter credentials. + * @param array $access_token + * @todo Ensure only one record per twitter_screen_name + */ + function save_twitter_user($access_token) { + $twitter_user = ORM::factory("twitter_user"); + $twitter_user->oauth_token = $access_token["oauth_token"]; + $twitter_user->oauth_token_secret = $access_token["oauth_token_secret"]; + $twitter_user->twitter_user_id = $access_token["user_id"]; + $twitter_user->screen_name = $access_token["screen_name"]; + $twitter_user->user_id = identity::active_user()->id; + $twitter_user->save(); + + message::success(t("Twitter access tokens saved!")); + } + + /** + * Redirect user to Twitter authorization page. + */ + function redirect() { + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + $oauth_callback = url::abs_site("twitter/callback"); + + // We'll want this after Twitter kicks back to our callback + if (!empty($_GET['item_url'])) { + Session::instance()->set("twitter_item_redirect", $_GET['item_url']); + } + + // Build TwitterOAuth object with client credentials + $connection = new TwitterOAuth($consumer_key, $consumer_secret); + + // Get temporary credentials. + $request_token = $connection->getRequestToken($oauth_callback); + + // Save temporary credentials to session. + Session::instance()->set("twitter_oauth_token", $request_token['oauth_token']); + Session::instance()->set("twitter_oauth_token_secret", $request_token['oauth_token_secret']); + + // If last connection failed don't display authorization link + if (200 == $connection->http_code) { + // Build authorize URL and redirect user to Twitter + $url = $connection->getAuthorizeURL($request_token["oauth_token"]); + url::redirect(url::site($url)); + } else { + // Show notification if something went wrong + message::success(t("Could not connect to Twitter. Refresh the page or try again later.")); + url::redirect(url::site($url)); + } + } + + /** + * Post a status update to Twitter + * @param string $message + */ + function tweet() { + access::verify_csrf(); + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $form = twitter::get_tweet_form(); + + $user_id = identity::active_user()->id; + $item_url = url::abs_site($item->relative_url_cache); + $twitter_user = $this->_get_twitter_user($user_id); + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + + $connection = new TwitterOAuth( + $consumer_key, + $consumer_secret, + $twitter_user["oauth_key"], + $twitter_user["oauth_user"]); + + $connection->post('statuses/update', array('status' => $message)); + + if (200 == $connection->http_code) { + return true; + } else { + // @todo Save tweet with a status of not sent. + return false; + } + + if (request::method() == "post") { + if ($form->validate()) { + $message = $form->twitter_message->tweet->value; + if ($this->post($message, $item)) { + message::success(t("Tweet sent!")); + } else { + message::error(t("Unable to send Tweet. Try again later.")); + } + } + url::redirect(url::abs_site($item->relative_url_cache)); + } + + } + + /** + * Clear Twitter module session variables + */ + function clear_twitter_session() { + Session::instance()->delete("twitter_oauth_token"); + Session::instance()->delete("twitter_oauth_token_secret"); + Session::instance()->delete("twitter_access_token"); + } +} \ No newline at end of file diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php new file mode 100644 index 00000000..4eb67789 --- /dev/null +++ b/3.1/modules/twitter/helpers/twitter.php @@ -0,0 +1,122 @@ + "g-configure-twitter-form")); + + $group_oauth = $form->group("twitter_oauth")->label(t("OAuth Settings")); + $group_oauth->input("consumer_key") + ->label(t("Consumer key")) + ->value(module::get_var("twitter", "consumer_key")); + $group_oauth->input("consumer_secret") + ->label(t("Consumer secret")) + ->value(module::get_var("twitter", "consumer_secret")); + + $group_tweet = $form->group("twitter_message")->label(t("Default Tweet")); + $group_tweet->input("default_tweet") + ->label("Default Tweet") + ->value(module::get_var("twitter", "default_tweet")); + // @todo Add reset default tweet button + + if (module::is_active("bitly")) { + $group_url = $form->group("urls")->label(t("Shorten URLs")); + $group_url->checkbox("shorten_urls") + ->label(t("Shorten URLs automatically with bit.ly")) + ->checked(module::get_var("twitter", "shorten_urls")); + } + + $form->submit("")->value(t("Save")); + return $form; + } + + /** + * + * @param $item + * @return Forge + */ + static function get_tweet_form($item) { + $long_url = url::abs_site($item->relative_url_cache); + $default_tweet = module::get_var("twitter", "default_tweet"); + $tweet = preg_replace("/%type/", $item->type, $default_tweet); + $tweet = preg_replace("/%title/", $item->title, $tweet); + $tweet = preg_replace("/%description/", $item->description, $tweet); + // If bit.ly module's enabled, get the item's URL and shorten it + // @todo Refactor bit.ly module so that it doesn't output a status message when called by other modules + if (module::is_active("bitly") && module::get_var("twitter", "shorten_urls")) { + $url = bitly::shorten_url($item->id); + } else { + $url = url::abs_site($item->relative_url_cache); + } + $tweet = preg_replace("/%url/", $url, $tweet); + $form = new Forge("twitter/tweet", "", "post", array("id" => "g-twitter-form")); + $group = $form->group("twitter_message")->label(t("Compose Tweet")); + $group->textarea("tweet") + ->value($tweet) + ->rules("required") + ->error_messages("required", t("Your tweet cannot be empty!")) + ->id("g-tweet"); + $group->hidden("item_id")->value($item->id); + $form->submit("")->value(t("Tweet")); + return $form; + } + + /** + * Has this Gallery been registered at dev.twitter.com/app? + * @return boolean + */ + static function is_registered() { + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + if (empty($consumer_key) || empty($consumer_secret)) { + site_status::warning( + t("Twitter module requires attention! Set the consumer key and secret.", + array("url" => html::mark_clean(url::site("admin/twitter")))), + "twitter_config"); + return false; + } else { + site_status::clear("twitter_config"); + return true; + } + } + + /** + * Reset the standard Tweet to the module default + * @return string + */ + static function reset_default_tweet() { + $default_tweet = t("Check out this %type, '%title': %description %url"); + module::set_var("twitter", "default_tweet", $default_tweet); + return $default_tweet; + } + +} diff --git a/3.1/modules/twitter/helpers/twitter_installer.php b/3.1/modules/twitter/helpers/twitter_installer.php new file mode 100644 index 00000000..7a4f77c6 --- /dev/null +++ b/3.1/modules/twitter/helpers/twitter_installer.php @@ -0,0 +1,50 @@ +query("CREATE TABLE {twitter_tweets} ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `created` int(9) NOT NULL, + `item_id` int(9) NOT NULL, + `status` tinyint(1) NOT NULL, + `tweet` varchar(140) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + Database::instance() + ->query("CREATE TABLE {twitter_users} ( + `id` int(9) NOT NULL AUTO_INCREMENT, + `oauth_token` varchar(64) NOT NULL, + `oauth_token_secret` varchar(64) NOT NULL, + `screen_name` varchar(16) NOT NULL, + `twitter_user_id` int(9) NOT NULL, + `user_id` int(9) NOT NULL, + PRIMARY KEY (`id`)) + DEFAULT CHARSET=utf8;"); + module::set_version("twitter", 1); + twitter::reset_default_tweet(); + } + + static function deactivate() { + site_status::clear("twitter_config"); + } +} From 086e8554e90db559999641faf8d3c5677e62d6f1 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 9 Feb 2011 18:40:55 -0700 Subject: [PATCH 275/300] Initial commit of Twitter module. OAuth access key retrieval and storage is working, posting tweets is not, yet. --- 3.1/modules/twitter/models/twitter_user.php | 21 ++++++++++++++++++ .../twitter/views/twitter_dialog.html.php | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 3.1/modules/twitter/models/twitter_user.php create mode 100644 3.1/modules/twitter/views/twitter_dialog.html.php diff --git a/3.1/modules/twitter/models/twitter_user.php b/3.1/modules/twitter/models/twitter_user.php new file mode 100644 index 00000000..fee9389f --- /dev/null +++ b/3.1/modules/twitter/models/twitter_user.php @@ -0,0 +1,21 @@ + + +
    +

    $type, "title"=> $title)) ?>

    + +

    + +

    +

    Sign in with Twitter

    + +
    + +
    + +
    +
    + +
    \ No newline at end of file From b7d8647316bb0fdeaa26fa203fc832dcc28e0524 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Wed, 9 Feb 2011 18:45:54 -0700 Subject: [PATCH 276/300] Removed unused class property. --- 3.1/modules/twitter/helpers/twitter.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php index 4eb67789..d0baf3a5 100644 --- a/3.1/modules/twitter/helpers/twitter.php +++ b/3.1/modules/twitter/helpers/twitter.php @@ -23,8 +23,6 @@ class twitter_Core { static $character_count = 140; - //public static $url = "http://twitter.com/home/?status="; - /** * * @return Forge From 00806139bb1ff97414b5a0914438749091795e07 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 11:43:46 -0700 Subject: [PATCH 277/300] Moved status messages from helper to controller, shouldn't generate them when other modules shorten links, i.e. Twitter, etc. Log bit.ly errors. Added README. --- 3.1/modules/bitly/controllers/bitly.php | 9 ++++++++- 3.1/modules/bitly/helpers/bitly.php | 10 ++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/3.1/modules/bitly/controllers/bitly.php b/3.1/modules/bitly/controllers/bitly.php index 8fe68d0a..cfa615c3 100644 --- a/3.1/modules/bitly/controllers/bitly.php +++ b/3.1/modules/bitly/controllers/bitly.php @@ -33,8 +33,15 @@ class bitly_Controller extends Controller { access::required("view", $item); access::required("edit", $item); - // Get the item's URL and shorten it + // Shorten the item's URL $short_url = bitly::shorten_url($item_id); + + if ($short_url) { + message::success("Item URL shortened to $short_url"); + } else { + + message::error("Unable to shorten " . url::abs_site($item->relative_url_cache)); + } // Redirect back to the item url::redirect(url::abs_site($item->relative_url_cache)); diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 526928fd..8ef8391a 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -187,8 +187,9 @@ class bitly_Core { $request = self::_build_http_request('shorten', $parameters); $response = self::_http_post($request, self::$api_host); $json_response = json_decode($response->body[0]); + $status_txt = $json_response->status_txt; - if ('OK' == $json_response->status_txt) { + if ('OK' == $status_txt) { $short_url = $json_response->data->url; // Save the link hash to the database $link = ORM::factory("bitly_link"); @@ -196,14 +197,11 @@ class bitly_Core { $link->hash = $json_response->data->hash; $link->global_hash = $json_response->data->global_hash; $link->save(); - - message::success("$long_url has been shortened to $short_url"); - return $json_response->data->url; } else { - message::error("Unable to shorten $long_url"); - // @todo log the error + $status_code = $json_response->status_code; + log::error("content", "Shortened URL", "Error: $status_code $status_txt item"); return false; } } From f37073b0c1dded53c800af969191cf4efd600dcf Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 11:49:20 -0700 Subject: [PATCH 278/300] Added README --- 3.1/modules/bitly/README | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 3.1/modules/bitly/README diff --git a/3.1/modules/bitly/README b/3.1/modules/bitly/README new file mode 100644 index 00000000..d89fe4fe --- /dev/null +++ b/3.1/modules/bitly/README @@ -0,0 +1,12 @@ +ABOUT: +Shorten Gallery's album and item links using bit.ly's URL shortening service. + +INSTALLATION AND CONFIGURATION INSTRUCTIONS: +http://codex.gallery2.org/Gallery3:Modules:bitly + +QUESTIONS, COMMENTS? +http://2tbsp.com/content/bitly-module-gallery-3 + +ROADMAP: +- Provide multi-user support. +- Display shortened link statistics (clicks, etc.) From e40d98ec8e5f7a263cbb8defa145c1c57d95956d Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 11:59:12 -0700 Subject: [PATCH 279/300] White-space and docblock updates. --- 3.1/modules/bitly/controllers/bitly.php | 1 - 3.1/modules/bitly/helpers/bitly.php | 19 ++++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/3.1/modules/bitly/controllers/bitly.php b/3.1/modules/bitly/controllers/bitly.php index cfa615c3..7a0b53c0 100644 --- a/3.1/modules/bitly/controllers/bitly.php +++ b/3.1/modules/bitly/controllers/bitly.php @@ -39,7 +39,6 @@ class bitly_Controller extends Controller { if ($short_url) { message::success("Item URL shortened to $short_url"); } else { - message::error("Unable to shorten " . url::abs_site($item->relative_url_cache)); } diff --git a/3.1/modules/bitly/helpers/bitly.php b/3.1/modules/bitly/helpers/bitly.php index 8ef8391a..f006362e 100644 --- a/3.1/modules/bitly/helpers/bitly.php +++ b/3.1/modules/bitly/helpers/bitly.php @@ -61,8 +61,8 @@ class bitly_Core { /** * Check a login and an API Key against bit.ly to make sure they're valid - * @param string $login the login - * @param string $api_key the API key + * @param string $login bit.ly login + * @param string $api_key bit.ly API key * @return boolean */ static function validate_config($login, $api_key) { @@ -115,15 +115,15 @@ class bitly_Core { } /** - * - * @param $type - * @param $parameters + * Assemble a bitly API request + * @param string $type Type of API request, ex. shorten + * @param array $params Query string key/value pairs * @return string */ - private static function _build_http_request($type, $parameters) { + private static function _build_http_request($type, $params) { $http_request = ''; - if (!empty($type) && count($parameters)) { - foreach($parameters as $k => $v) { + if (!empty($type) && count($params)) { + foreach($params as $k => $v) { $query_string[] = "$k=" . urlencode($v); } $path = "/" . self::$api_version . "/$type?" . implode('&', $query_string); @@ -175,7 +175,6 @@ class bitly_Core { $item = ORM::factory("item", $item_id); $short_url = ''; $long_url = url::abs_site($item->relative_url_cache); - $parameters = array( "login" => module::get_var("bitly", "login"), 'apiKey' => module::get_var("bitly", "api_key"), @@ -183,7 +182,6 @@ class bitly_Core { 'domain' => module::get_var("bitly", "domain"), 'format' => $format, ); - $request = self::_build_http_request('shorten', $parameters); $response = self::_http_post($request, self::$api_host); $json_response = json_decode($response->body[0]); @@ -198,7 +196,6 @@ class bitly_Core { $link->global_hash = $json_response->data->global_hash; $link->save(); return $json_response->data->url; - } else { $status_code = $json_response->status_code; log::error("content", "Shortened URL", "Error: $status_code $status_txt item"); From bdad42535fc33f6995bcea43cbbf2453653949a4 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 17:17:05 -0700 Subject: [PATCH 280/300] Tweets are now being sent, but the page redirect/dialog is not handled properly on success, or failure. Added a reset the default tweet to the default tweet value to the admin form. --- 3.1/modules/twitter/controllers/twitter.php | 68 +++++++++---------- 3.1/modules/twitter/helpers/twitter.php | 33 +++------ .../twitter/views/admin_twitter.html.php | 4 +- 3 files changed, 47 insertions(+), 58 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index e55ed674..1eee87a9 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -183,45 +183,45 @@ class Twitter_Controller extends Controller { * Post a status update to Twitter * @param string $message */ - function tweet() { + public function tweet($item_id) { access::verify_csrf(); - require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $item = ORM::factory("item", $item_id); + $form = twitter::get_tweet_form($item); + + if ($form->validate()) { + echo "Validated!"; + $user_id = identity::active_user()->id; + $item_url = url::abs_site($item->relative_url_cache); + $twitter_user = $this->_get_twitter_user($user_id); + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); - $form = twitter::get_tweet_form(); + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $connection = new TwitterOAuth( + $consumer_key, + $consumer_secret, + $twitter_user->oauth_token, + $twitter_user->oauth_token_secret); + + $message = $form->twitter_message->tweet->value; + $connection->post('statuses/update', array('status' => $message)); - $user_id = identity::active_user()->id; - $item_url = url::abs_site($item->relative_url_cache); - $twitter_user = $this->_get_twitter_user($user_id); - $consumer_key = module::get_var("twitter", "consumer_key"); - $consumer_secret = module::get_var("twitter", "consumer_secret"); - - $connection = new TwitterOAuth( - $consumer_key, - $consumer_secret, - $twitter_user["oauth_key"], - $twitter_user["oauth_user"]); - - $connection->post('statuses/update', array('status' => $message)); - - if (200 == $connection->http_code) { - return true; + if (200 == $connection->http_code) { + message::success(t("Tweet sent!")); + //url::redirect(url::abs_site($item->relative_url_cache)); + //json::reply(array("result" => "success")); + json::reply(array("result" => "success", "location" => $item->url())); + } else { + message::error(t("Unable to send Tweet. Try again later.")); + json::reply(array("result" => "error", "html" => (string)$form)); + // @todo Save tweet with a status of not sent. + } } else { - // @todo Save tweet with a status of not sent. - return false; + echo "validation failed"; + json::reply(array("result" => "error", "html" => (string)$form)); } - - if (request::method() == "post") { - if ($form->validate()) { - $message = $form->twitter_message->tweet->value; - if ($this->post($message, $item)) { - message::success(t("Tweet sent!")); - } else { - message::error(t("Unable to send Tweet. Try again later.")); - } - } - url::redirect(url::abs_site($item->relative_url_cache)); - } - } /** diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php index d0baf3a5..f222c170 100644 --- a/3.1/modules/twitter/helpers/twitter.php +++ b/3.1/modules/twitter/helpers/twitter.php @@ -24,10 +24,9 @@ class twitter_Core { static $character_count = 140; /** - * - * @return Forge + * Get module configure form + * @return Forge * @todo Set global Twitter account - * @todo Default tweet message */ static function get_configure_form() { $form = new Forge("admin/twitter", "", "post", array("id" => "g-configure-twitter-form")); @@ -42,9 +41,11 @@ class twitter_Core { $group_tweet = $form->group("twitter_message")->label(t("Default Tweet")); $group_tweet->input("default_tweet") - ->label("Default Tweet") + ->label(t("Default Tweet")) ->value(module::get_var("twitter", "default_tweet")); - // @todo Add reset default tweet button + $group_tweet->checkbox("reset_tweet") + ->label(t("Reset to default on save")) + ->value(1); if (module::is_active("bitly")) { $group_url = $form->group("urls")->label(t("Shorten URLs")); @@ -52,14 +53,13 @@ class twitter_Core { ->label(t("Shorten URLs automatically with bit.ly")) ->checked(module::get_var("twitter", "shorten_urls")); } - $form->submit("")->value(t("Save")); return $form; } /** - * - * @param $item + * Get tweet form + * @param object $item * @return Forge */ static function get_tweet_form($item) { @@ -69,21 +69,20 @@ class twitter_Core { $tweet = preg_replace("/%title/", $item->title, $tweet); $tweet = preg_replace("/%description/", $item->description, $tweet); // If bit.ly module's enabled, get the item's URL and shorten it - // @todo Refactor bit.ly module so that it doesn't output a status message when called by other modules - if (module::is_active("bitly") && module::get_var("twitter", "shorten_urls")) { + if (!empty($item->id) && module::is_active("bitly") && module::get_var("twitter", "shorten_urls")) { $url = bitly::shorten_url($item->id); } else { $url = url::abs_site($item->relative_url_cache); } $tweet = preg_replace("/%url/", $url, $tweet); - $form = new Forge("twitter/tweet", "", "post", array("id" => "g-twitter-form")); + + $form = new Forge("twitter/tweet/$item->id", "", "post", array("id" => "g-twitter-tweet-form")); $group = $form->group("twitter_message")->label(t("Compose Tweet")); $group->textarea("tweet") ->value($tweet) ->rules("required") ->error_messages("required", t("Your tweet cannot be empty!")) ->id("g-tweet"); - $group->hidden("item_id")->value($item->id); $form->submit("")->value(t("Tweet")); return $form; } @@ -107,14 +106,4 @@ class twitter_Core { } } - /** - * Reset the standard Tweet to the module default - * @return string - */ - static function reset_default_tweet() { - $default_tweet = t("Check out this %type, '%title': %description %url"); - module::set_var("twitter", "default_tweet", $default_tweet); - return $default_tweet; - } - } diff --git a/3.1/modules/twitter/views/admin_twitter.html.php b/3.1/modules/twitter/views/admin_twitter.html.php index c70c2f46..807a72df 100644 --- a/3.1/modules/twitter/views/admin_twitter.html.php +++ b/3.1/modules/twitter/views/admin_twitter.html.php @@ -16,8 +16,8 @@

    -

    Twitter application settings.", +

    Twitter application settings, if necessary.", array("twitter_apps" => "http://dev.twitter.com/apps")) ?>

    bit.ly module to shorten From 815cbd4234651d94a22a0e7c13064c6dc38a8a12 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 17:42:37 -0700 Subject: [PATCH 281/300] Added a reset the default tweet to the default tweet value to the admin form. --- .../twitter/controllers/admin_twitter.php | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/3.1/modules/twitter/controllers/admin_twitter.php b/3.1/modules/twitter/controllers/admin_twitter.php index b2f61ab8..a65168fb 100644 --- a/3.1/modules/twitter/controllers/admin_twitter.php +++ b/3.1/modules/twitter/controllers/admin_twitter.php @@ -19,9 +19,16 @@ */ class Admin_Twitter_Controller extends Admin_Controller { + public $default_tweet; + + function __construct() { + parent::__construct(); + $this->default_tweet = t("Check out this %type, '%title': %description %url"); + } + /** * bit.ly module's settings - * @todo Create/get and display the shortened value for this Gallery's root album (home page) + * @todo Show default tweet value after resetting it! */ public function index() { $form = twitter::get_configure_form(); @@ -30,7 +37,13 @@ class Admin_Twitter_Controller extends Admin_Controller { if ($form->validate()) { $consumer_key = $form->twitter_oauth->consumer_key->value; $consumer_secret = $form->twitter_oauth->consumer_secret->value; - $default_tweet = $form->twitter_message->default_tweet->value; + $reset_tweet = $form->twitter_message->reset_tweet->value; + if ($reset_tweet) { + $default_tweet = $this->default_tweet; + $form->twitter_message->default_tweet->value = $this->default_tweet; + } else { + $default_tweet = $form->twitter_message->default_tweet->value; + } $shorten_urls = $form->urls->shorten_urls->value; module::set_var("twitter", "consumer_key", $consumer_key); From 31b9f57a090af3df51b46b0c0d5e02a8766d09f7 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 18:26:56 -0700 Subject: [PATCH 282/300] Don't show Tweet this menu items if the Twitter consumer key and secret are not sent. --- 3.1/modules/twitter/helpers/twitter_event.php | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/3.1/modules/twitter/helpers/twitter_event.php b/3.1/modules/twitter/helpers/twitter_event.php index eabb047e..37c923a9 100644 --- a/3.1/modules/twitter/helpers/twitter_event.php +++ b/3.1/modules/twitter/helpers/twitter_event.php @@ -29,22 +29,25 @@ class twitter_event_Core { static function site_menu($menu, $theme) { $item = $theme->item(); - - $menu->get("options_menu") - ->append(Menu::factory("dialog") - ->id("twitter") - ->label(t("Share on Twitter")) - ->css_id("g-twitter-link") - ->url(url::site("twitter/dialog/{$item->id}"))); + if (twitter::is_registered()) { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("twitter") + ->label(t("Share on Twitter")) + ->css_id("g-twitter-link") + ->url(url::site("twitter/dialog/{$item->id}"))); + } } static function context_menu($menu, $theme, $item) { - $menu->get("options_menu") - ->append(Menu::factory("dialog") - ->id("twitter") - ->label(t("Share on Twitter")) - ->css_class("ui-icon-link g-twitter-share") - ->url(url::site("twitter/dialog/{$item->id}"))); + if (twitter::is_registered()) { + $menu->get("options_menu") + ->append(Menu::factory("dialog") + ->id("twitter") + ->label(t("Share on Twitter")) + ->css_class("ui-icon-link g-twitter-share") + ->url(url::site("twitter/dialog/{$item->id}"))); + } } } From 8eab4773677f85bdc42634f3e550540e849ae04a Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sat, 12 Feb 2011 19:00:17 -0700 Subject: [PATCH 283/300] Save tweets sent successfully, and those which weren't sent. --- 3.1/modules/twitter/controllers/twitter.php | 34 ++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index 1eee87a9..36cbb722 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -179,6 +179,24 @@ class Twitter_Controller extends Controller { } } + /** + * Save tweets sent and those not sent because of Twitter API issues + * @param integer $item_id + * @param string $tweet The tweet sent, or the tweet that couldn't be sent + * @param boolean $status 1 for success and 0 for not sent + */ + public function save($item_id, $tweet, $status) { + if (!empty($item_id) && !empty($tweet) && !empty($status)) { + $t = ORM::factory("twitter_tweet"); + $t->created = time(); + $t->item_id = $item_id; + $t->tweet = $tweet; + $t->status = $status; + $t->user_id = identity::active_user()->id; + $t->save(); + } + } + /** * Post a status update to Twitter * @param string $message @@ -190,10 +208,8 @@ class Twitter_Controller extends Controller { $form = twitter::get_tweet_form($item); if ($form->validate()) { - echo "Validated!"; - $user_id = identity::active_user()->id; $item_url = url::abs_site($item->relative_url_cache); - $twitter_user = $this->_get_twitter_user($user_id); + $twitter_user = $this->_get_twitter_user(identity::active_user()->id); $consumer_key = module::get_var("twitter", "consumer_key"); $consumer_secret = module::get_var("twitter", "consumer_secret"); @@ -213,11 +229,15 @@ class Twitter_Controller extends Controller { //url::redirect(url::abs_site($item->relative_url_cache)); //json::reply(array("result" => "success")); json::reply(array("result" => "success", "location" => $item->url())); + $status = 1; } else { - message::error(t("Unable to send Tweet. Try again later.")); - json::reply(array("result" => "error", "html" => (string)$form)); - // @todo Save tweet with a status of not sent. - } + message::error(t("Unable to send Tweet. Your message has been saved. Please try again later.")); + json::reply(array("result" => "error", "location" => $item->url())); + $status = 0; + // @todo Log Twitter error response + } + $this->save($item_id, $message, $status); + } else { echo "validation failed"; json::reply(array("result" => "error", "html" => (string)$form)); From 5c3e9690dfc146a7b70926d23ac2b6e24497c3e2 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 12:26:19 -0700 Subject: [PATCH 284/300] Store Twitter status id. --- 3.1/modules/twitter/helpers/twitter_installer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/3.1/modules/twitter/helpers/twitter_installer.php b/3.1/modules/twitter/helpers/twitter_installer.php index 7a4f77c6..8cf4363c 100644 --- a/3.1/modules/twitter/helpers/twitter_installer.php +++ b/3.1/modules/twitter/helpers/twitter_installer.php @@ -26,6 +26,7 @@ class twitter_installer { `created` int(9) NOT NULL, `item_id` int(9) NOT NULL, `status` tinyint(1) NOT NULL, + `twitter_id` decimal(20,0) NULL, `tweet` varchar(140) NOT NULL, `user_id` int(9) NOT NULL, PRIMARY KEY (`id`)) From 1234f8c3ff5a70f14d2a5d1e70153e36ec11ecc0 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 13:11:42 -0700 Subject: [PATCH 285/300] Save successful and failed Tweets. Load latest failed tweet for an item on subsequent attempts. Delete failed tweets for an item once a tweet is sent for that item successfully. Code cleanup and organization updates. --- 3.1/modules/twitter/controllers/twitter.php | 253 +++++++++++--------- 3.1/modules/twitter/helpers/twitter.php | 31 ++- 2 files changed, 170 insertions(+), 114 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index 36cbb722..6804ff6a 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -20,6 +20,52 @@ class Twitter_Controller extends Controller { + /** + * Verify credentials and redirect based on response from Twitter. + */ + public function callback() { + require_once(MODPATH . "twitter/lib/twitteroauth.php"); + + $consumer_key = module::get_var("twitter", "consumer_key"); + $consumer_secret = module::get_var("twitter", "consumer_secret"); + $oauth_token = Session::instance()->get("twitter_oauth_token"); + $oauth_token_secret = Session::instance()->get("twitter_oauth_token_secret"); + $item_url = Session::instance()->get("twitter_item_redirect"); + + // If the oauth_token is old redirect to the connect page + if (isset($_REQUEST['oauth_token']) && $oauth_token !== $_REQUEST['oauth_token']) { + Session::instance()->set("twitter_oauth_status", "old_token"); + $this->_clear_session(); + url::redirect(url::site("twitter/redirect")); + } + + // Create TwitteroAuth object with app key/secret and token key/secret from default phase + $connection = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); + + // Request access tokens from twitter + $access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']); + + // Save the access tokens + Session::instance()->set("twitter_access_token", $access_token); + + // Remove no longer needed request tokens + Session::instance()->delete("twitter_oauth_token"); + Session::instance()->delete("twitter_oauth_token_secret"); + + // If HTTP response is 200 continue otherwise send to connect page to retry + if (200 == $connection->http_code) { + // The user has been verified and the access tokens can be saved for future use + $this->save_user($access_token); + // Redirect to the tweet form + $item = ORM::factory("item", $item_id); + url::redirect(url::abs_site($item_url)); + } else { + // @todo Log HTTP status for application log and/or error message + $this->_clear_session(); + url::redirect(url::site("twitter/redirect")); + } + } + /** * Display Twitter status dialog. * @param int $item_id @@ -49,99 +95,9 @@ class Twitter_Controller extends Controller { $v->user_id = $user_id; $v->twitter_auth_url = url::site("twitter/redirect?item_url=$item_url"); } - print $v; } - /** - * Check if current user's Twitter credentials have been stored locally. - * @param int $user_id - * @return boolean - */ - private function _is_token_set($user_id) { - $twitter_user = $this->_get_twitter_user($user_id); - if (!empty($twitter_user->oauth_token) && !empty($twitter_user->oauth_token_secret)) { - return true; - } - return false; - } - - /** - * Get Twitter credentials for the current user. - * @param int $user_id - * @return mixed object|false - */ - private function _get_twitter_user($user_id) { - $twitter_user = ORM::factory("twitter_user")->where("user_id", "=", $user_id)->find(); - if ($twitter_user->loaded()) { - return $twitter_user; - } - return false; - } - - /** - * Verify credentials and redirect based on response from Twitter. - */ - public function callback() { - require_once(MODPATH . "twitter/lib/twitteroauth.php"); - - $consumer_key = module::get_var("twitter", "consumer_key"); - $consumer_secret = module::get_var("twitter", "consumer_secret"); - $oauth_token = Session::instance()->get("twitter_oauth_token"); - $oauth_token_secret = Session::instance()->get("twitter_oauth_token_secret"); - $item_url = Session::instance()->get("twitter_item_redirect"); - - // If the oauth_token is old redirect to the connect page - if (isset($_REQUEST['oauth_token']) && $oauth_token !== $_REQUEST['oauth_token']) { - Session::instance()->set("twitter_oauth_status", "old_token"); - $this->clear_twitter_session(); - url::redirect(url::site("twitter/redirect")); - } - - // Create TwitteroAuth object with app key/secret and token key/secret from default phase - $connection = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret); - - // Request access tokens from twitter - $access_token = $connection->getAccessToken($_REQUEST['oauth_verifier']); - - // Save the access tokens - Session::instance()->set("twitter_access_token", $access_token); - - // Remove no longer needed request tokens - Session::instance()->delete("twitter_oauth_token"); - Session::instance()->delete("twitter_oauth_token_secret"); - - // If HTTP response is 200 continue otherwise send to connect page to retry - if (200 == $connection->http_code) { - // The user has been verified and the access tokens can be saved for future use - $this->save_twitter_user($access_token); - // Redirect to the tweet form - $item = ORM::factory("item", $item_id); - url::redirect(url::abs_site($item_url)); - } else { - // @todo Log HTTP status for application log and/or error message - $this->clear_twitter_session(); - url::redirect(url::site("twitter/redirect")); - } - } - - /** - * Save or update the current user's Twitter credentials. - * @param array $access_token - * @todo Ensure only one record per twitter_screen_name - */ - function save_twitter_user($access_token) { - $twitter_user = ORM::factory("twitter_user"); - $twitter_user->oauth_token = $access_token["oauth_token"]; - $twitter_user->oauth_token_secret = $access_token["oauth_token_secret"]; - $twitter_user->twitter_user_id = $access_token["user_id"]; - $twitter_user->screen_name = $access_token["screen_name"]; - $twitter_user->user_id = identity::active_user()->id; - $twitter_user->save(); - - message::success(t("Twitter access tokens saved!")); - } - /** * Redirect user to Twitter authorization page. */ @@ -182,24 +138,42 @@ class Twitter_Controller extends Controller { /** * Save tweets sent and those not sent because of Twitter API issues * @param integer $item_id - * @param string $tweet The tweet sent, or the tweet that couldn't be sent - * @param boolean $status 1 for success and 0 for not sent + * @param object $tweet The tweet sent, or the tweet that couldn't be sent */ - public function save($item_id, $tweet, $status) { - if (!empty($item_id) && !empty($tweet) && !empty($status)) { + public function save_tweet($tweet) { + if (!empty($tweet->item_id) && !empty($tweet->tweet) && !empty($tweet->status)) { $t = ORM::factory("twitter_tweet"); $t->created = time(); - $t->item_id = $item_id; - $t->tweet = $tweet; - $t->status = $status; + $t->item_id = $tweet->item_id; + $t->twitter_id = $tweet->twitter_id; + $t->tweet = $tweet->tweet; + $t->status = $tweet->status; $t->user_id = identity::active_user()->id; $t->save(); } } + /** + * Save or update the current user's Twitter credentials. + * @param array $access_token + * @todo Ensure only one record per twitter_screen_name + */ + function save_user($access_token) { + $u = ORM::factory("twitter_user"); + $u->oauth_token = $access_token["oauth_token"]; + $u->oauth_token_secret = $access_token["oauth_token_secret"]; + $u->twitter_user_id = $access_token["user_id"]; + $u->screen_name = $access_token["screen_name"]; + $u->user_id = identity::active_user()->id; + $u->save(); + + message::success(t("Twitter access tokens saved!")); + } + /** * Post a status update to Twitter * @param string $message + * @todo Update previously failed tweet, if one exists */ public function tweet($item_id) { access::verify_csrf(); @@ -209,7 +183,7 @@ class Twitter_Controller extends Controller { if ($form->validate()) { $item_url = url::abs_site($item->relative_url_cache); - $twitter_user = $this->_get_twitter_user(identity::active_user()->id); + $u = $this->_get_twitter_user(identity::active_user()->id); $consumer_key = module::get_var("twitter", "consumer_key"); $consumer_secret = module::get_var("twitter", "consumer_secret"); @@ -218,38 +192,93 @@ class Twitter_Controller extends Controller { $connection = new TwitterOAuth( $consumer_key, $consumer_secret, - $twitter_user->oauth_token, - $twitter_user->oauth_token_secret); + $u->oauth_token, + $u->oauth_token_secret); $message = $form->twitter_message->tweet->value; - $connection->post('statuses/update', array('status' => $message)); + $response = $connection->post('statuses/update', array('status' => $message)); if (200 == $connection->http_code) { - message::success(t("Tweet sent!")); - //url::redirect(url::abs_site($item->relative_url_cache)); - //json::reply(array("result" => "success")); - json::reply(array("result" => "success", "location" => $item->url())); $status = 1; + message::success(t("Tweet sent!")); + json::reply(array("result" => "success", "location" => $item->url())); } else { - message::error(t("Unable to send Tweet. Your message has been saved. Please try again later.")); - json::reply(array("result" => "error", "location" => $item->url())); $status = 0; - // @todo Log Twitter error response + log::error("content", "Twitter", "Unable to sent tweet, response code: " . $connection->http_code); + message::error(t("Unable to send Tweet. Your message has been saved. Please try again later.")); + json::reply(array("result" => "error", "html" => (string)$form)); } - $this->save($item_id, $message, $status); + $tweet->item_id = $item_id; + $tweet->twitter_id = $response->id; + $tweet->tweet = $message; + $tweet->status = $status; + + $this->save_tweet($tweet); + $this->_delete_failed($item_id); } else { - echo "validation failed"; json::reply(array("result" => "error", "html" => (string)$form)); } } + /** + * + * @param $tweet + */ + function update_tweet($tweet) { + + } + /** * Clear Twitter module session variables */ - function clear_twitter_session() { + private function _clear_session() { Session::instance()->delete("twitter_oauth_token"); Session::instance()->delete("twitter_oauth_token_secret"); Session::instance()->delete("twitter_access_token"); } -} \ No newline at end of file + + /** + * Delete all failed tweets by the current user for an item + * @param integer $item_id + * @todo Not implemented + */ + private function _delete_failed($item_id) { + if (is_numeric($item_id)) { + $user_id = identity::active_user()->id; + $result = db::build() + ->delete("twitter_tweets") + ->where("user_id", "=", $user_id) + ->where("item_id", "=", $item_id) + ->where("status", "=", 0) + ->execute(); + } + } + + /** + * Get Twitter credentials for the current user. + * @param int $user_id + * @return mixed object|false + */ + private function _get_twitter_user($user_id) { + $twitter_user = ORM::factory("twitter_user")->where("user_id", "=", $user_id)->find(); + if ($twitter_user->loaded()) { + return $twitter_user; + } + return false; + } + + /** + * Check if current user's Twitter credentials have been stored locally. + * @param int $user_id + * @return boolean + */ + private function _is_token_set($user_id) { + $twitter_user = $this->_get_twitter_user($user_id); + if (!empty($twitter_user->oauth_token) && !empty($twitter_user->oauth_token_secret)) { + return true; + } + return false; + } + +} diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php index f222c170..f523dd3f 100644 --- a/3.1/modules/twitter/helpers/twitter.php +++ b/3.1/modules/twitter/helpers/twitter.php @@ -60,11 +60,18 @@ class twitter_Core { /** * Get tweet form * @param object $item - * @return Forge + * @return Forge + * @todo Load previously failed tweet for the current user for this item */ static function get_tweet_form($item) { $long_url = url::abs_site($item->relative_url_cache); - $default_tweet = module::get_var("twitter", "default_tweet"); + // Check for saved tweets for this user and item + $saved_tweet = self::get_failed($item->id); + if ($saved_tweet) { + $default_tweet = $saved_tweet; + } else { + $default_tweet = module::get_var("twitter", "default_tweet"); + } $tweet = preg_replace("/%type/", $item->type, $default_tweet); $tweet = preg_replace("/%title/", $item->title, $tweet); $tweet = preg_replace("/%description/", $item->description, $tweet); @@ -87,6 +94,26 @@ class twitter_Core { return $form; } + /** + * Get the most recent failed tweet for an item + * @param integer $item_id + * @return mixed object|false + * @todo Not implemented + */ + function get_failed($item_id) { + $user_id = identity::active_user()->id; + $t = ORM::factory("twitter_tweet") + ->where("item_id", "=", $item_id) + ->where("user_id", "=", $user_id) + ->where("status", "=", 0) + ->find(); + if ($t->loaded()) { + return $t->tweet; + } else { + return false; + } + } + /** * Has this Gallery been registered at dev.twitter.com/app? * @return boolean From b8d92a59af828a6a4fff11329fa65711d422466d Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 13:53:39 -0700 Subject: [PATCH 286/300] Tweets table updates. Dropped 'status', instead look for empty twitter_id to indicate whether a tweet was sent successfully. Also changed 'created' to 'sent' and only set it after a tweet is successfully sent. --- 3.1/modules/twitter/controllers/twitter.php | 117 ++++++++---------- 3.1/modules/twitter/helpers/twitter.php | 3 +- .../twitter/helpers/twitter_installer.php | 5 +- 3 files changed, 58 insertions(+), 67 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index 6804ff6a..8744d1c0 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -54,13 +54,11 @@ class Twitter_Controller extends Controller { // If HTTP response is 200 continue otherwise send to connect page to retry if (200 == $connection->http_code) { - // The user has been verified and the access tokens can be saved for future use - $this->save_user($access_token); - // Redirect to the tweet form + $this->_save_user($access_token); $item = ORM::factory("item", $item_id); url::redirect(url::abs_site($item_url)); } else { - // @todo Log HTTP status for application log and/or error message + log::error("content", "Twitter", "Unable to retrieve user access token: " . $connection->http_code); $this->_clear_session(); url::redirect(url::site("twitter/redirect")); } @@ -101,7 +99,7 @@ class Twitter_Controller extends Controller { /** * Redirect user to Twitter authorization page. */ - function redirect() { + public function redirect() { require_once(MODPATH . "twitter/lib/twitteroauth.php"); $consumer_key = module::get_var("twitter", "consumer_key"); @@ -135,45 +133,11 @@ class Twitter_Controller extends Controller { } } - /** - * Save tweets sent and those not sent because of Twitter API issues - * @param integer $item_id - * @param object $tweet The tweet sent, or the tweet that couldn't be sent - */ - public function save_tweet($tweet) { - if (!empty($tweet->item_id) && !empty($tweet->tweet) && !empty($tweet->status)) { - $t = ORM::factory("twitter_tweet"); - $t->created = time(); - $t->item_id = $tweet->item_id; - $t->twitter_id = $tweet->twitter_id; - $t->tweet = $tweet->tweet; - $t->status = $tweet->status; - $t->user_id = identity::active_user()->id; - $t->save(); - } - } - - /** - * Save or update the current user's Twitter credentials. - * @param array $access_token - * @todo Ensure only one record per twitter_screen_name - */ - function save_user($access_token) { - $u = ORM::factory("twitter_user"); - $u->oauth_token = $access_token["oauth_token"]; - $u->oauth_token_secret = $access_token["oauth_token_secret"]; - $u->twitter_user_id = $access_token["user_id"]; - $u->screen_name = $access_token["screen_name"]; - $u->user_id = identity::active_user()->id; - $u->save(); - - message::success(t("Twitter access tokens saved!")); - } - /** * Post a status update to Twitter - * @param string $message + * @param int $item_id * @todo Update previously failed tweet, if one exists + * @todo Display errors in Tweet dialog */ public function tweet($item_id) { access::verify_csrf(); @@ -183,7 +147,7 @@ class Twitter_Controller extends Controller { if ($form->validate()) { $item_url = url::abs_site($item->relative_url_cache); - $u = $this->_get_twitter_user(identity::active_user()->id); + $user = $this->_get_twitter_user(identity::active_user()->id); $consumer_key = module::get_var("twitter", "consumer_key"); $consumer_secret = module::get_var("twitter", "consumer_secret"); @@ -192,28 +156,25 @@ class Twitter_Controller extends Controller { $connection = new TwitterOAuth( $consumer_key, $consumer_secret, - $u->oauth_token, - $u->oauth_token_secret); + $user->oauth_token, + $user->oauth_token_secret); $message = $form->twitter_message->tweet->value; $response = $connection->post('statuses/update', array('status' => $message)); if (200 == $connection->http_code) { - $status = 1; message::success(t("Tweet sent!")); json::reply(array("result" => "success", "location" => $item->url())); } else { - $status = 0; - log::error("content", "Twitter", "Unable to sent tweet, response code: " . $connection->http_code); + log::error("content", "Twitter", "Unable to send tweet: " . $connection->http_code); message::error(t("Unable to send Tweet. Your message has been saved. Please try again later.")); json::reply(array("result" => "error", "html" => (string)$form)); } $tweet->item_id = $item_id; - $tweet->twitter_id = $response->id; + (!empty($response->id)) ? $tweet->twitter_id = $response->id : $tweet->twitter_id = NULL; $tweet->tweet = $message; - $tweet->status = $status; - $this->save_tweet($tweet); + $this->_save_tweet($tweet); $this->_delete_failed($item_id); } else { @@ -221,14 +182,6 @@ class Twitter_Controller extends Controller { } } - /** - * - * @param $tweet - */ - function update_tweet($tweet) { - - } - /** * Clear Twitter module session variables */ @@ -240,8 +193,7 @@ class Twitter_Controller extends Controller { /** * Delete all failed tweets by the current user for an item - * @param integer $item_id - * @todo Not implemented + * @param int $item_id */ private function _delete_failed($item_id) { if (is_numeric($item_id)) { @@ -250,14 +202,14 @@ class Twitter_Controller extends Controller { ->delete("twitter_tweets") ->where("user_id", "=", $user_id) ->where("item_id", "=", $item_id) - ->where("status", "=", 0) + ->where("twitter_id", "=", "") ->execute(); } } /** * Get Twitter credentials for the current user. - * @param int $user_id + * @param int $user_id * @return mixed object|false */ private function _get_twitter_user($user_id) { @@ -281,4 +233,45 @@ class Twitter_Controller extends Controller { return false; } + /** + * Save new tweets + * @param object $tweet + */ + private function _save_tweet($tweet) { + if (!empty($tweet->item_id) && !empty($tweet->tweet)) { + $t = ORM::factory("twitter_tweet"); + $t->item_id = $tweet->item_id; + $t->twitter_id = $tweet->twitter_id; + $t->tweet = $tweet->tweet; + $t->sent = (!empty($tweet->twitter_id)) ? time() : NULL; + $t->user_id = identity::active_user()->id; + $t->save(); + } + } + + /** + * Save or update the current user's Twitter credentials. + * @param array $access_token + * @todo Ensure only one record per twitter_screen_name + */ + private function _save_user($access_token) { + $u = ORM::factory("twitter_user"); + $u->oauth_token = $access_token["oauth_token"]; + $u->oauth_token_secret = $access_token["oauth_token_secret"]; + $u->twitter_user_id = $access_token["user_id"]; + $u->screen_name = $access_token["screen_name"]; + $u->user_id = identity::active_user()->id; + $u->save(); + + message::success(t("Twitter access tokens saved!")); + } + + /** + * Update a previously failed tweet + * @param object $tweet + */ + private function _update_tweet($tweet) { + + } + } diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php index f523dd3f..59dc16c4 100644 --- a/3.1/modules/twitter/helpers/twitter.php +++ b/3.1/modules/twitter/helpers/twitter.php @@ -98,14 +98,13 @@ class twitter_Core { * Get the most recent failed tweet for an item * @param integer $item_id * @return mixed object|false - * @todo Not implemented */ function get_failed($item_id) { $user_id = identity::active_user()->id; $t = ORM::factory("twitter_tweet") ->where("item_id", "=", $item_id) ->where("user_id", "=", $user_id) - ->where("status", "=", 0) + ->where("twitter_id", "=", "") ->find(); if ($t->loaded()) { return $t->tweet; diff --git a/3.1/modules/twitter/helpers/twitter_installer.php b/3.1/modules/twitter/helpers/twitter_installer.php index 8cf4363c..bff04bb4 100644 --- a/3.1/modules/twitter/helpers/twitter_installer.php +++ b/3.1/modules/twitter/helpers/twitter_installer.php @@ -23,12 +23,11 @@ class twitter_installer { Database::instance() ->query("CREATE TABLE {twitter_tweets} ( `id` int(9) NOT NULL AUTO_INCREMENT, - `created` int(9) NOT NULL, `item_id` int(9) NOT NULL, - `status` tinyint(1) NOT NULL, `twitter_id` decimal(20,0) NULL, `tweet` varchar(140) NOT NULL, - `user_id` int(9) NOT NULL, + `sent` int(9) NULL, + `created` int(9) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); Database::instance() From 00b1f375c6b647dceb17c925dc1815d3a1390916 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 14:10:40 -0700 Subject: [PATCH 287/300] Expand the size of the tweet column until I better understand how G3 handles UTP-8 and Twitter's tweet length rules and UTF-8. --- 3.1/modules/twitter/helpers/twitter_installer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/twitter/helpers/twitter_installer.php b/3.1/modules/twitter/helpers/twitter_installer.php index bff04bb4..4e256b64 100644 --- a/3.1/modules/twitter/helpers/twitter_installer.php +++ b/3.1/modules/twitter/helpers/twitter_installer.php @@ -25,7 +25,7 @@ class twitter_installer { `id` int(9) NOT NULL AUTO_INCREMENT, `item_id` int(9) NOT NULL, `twitter_id` decimal(20,0) NULL, - `tweet` varchar(140) NOT NULL, + `tweet` varchar(255) NOT NULL, `sent` int(9) NULL, `created` int(9) NOT NULL, PRIMARY KEY (`id`)) From a5907ea38e4d866284f62e4dad516b307d2ae39f Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 14:56:24 -0700 Subject: [PATCH 288/300] Update previously failed tweet for an item upon a subsequent successful tweet. --- 3.1/modules/twitter/controllers/twitter.php | 36 ++++----------------- 3.1/modules/twitter/helpers/twitter.php | 15 +++++---- 2 files changed, 15 insertions(+), 36 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index 8744d1c0..870df2b5 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -136,8 +136,7 @@ class Twitter_Controller extends Controller { /** * Post a status update to Twitter * @param int $item_id - * @todo Update previously failed tweet, if one exists - * @todo Display errors in Tweet dialog + * @todo Display Twitter API errors in Tweet dialog */ public function tweet($item_id) { access::verify_csrf(); @@ -173,9 +172,8 @@ class Twitter_Controller extends Controller { $tweet->item_id = $item_id; (!empty($response->id)) ? $tweet->twitter_id = $response->id : $tweet->twitter_id = NULL; $tweet->tweet = $message; - + $tweet->id = $form->twitter_message->tweet_id->value; $this->_save_tweet($tweet); - $this->_delete_failed($item_id); } else { json::reply(array("result" => "error", "html" => (string)$form)); @@ -191,22 +189,6 @@ class Twitter_Controller extends Controller { Session::instance()->delete("twitter_access_token"); } - /** - * Delete all failed tweets by the current user for an item - * @param int $item_id - */ - private function _delete_failed($item_id) { - if (is_numeric($item_id)) { - $user_id = identity::active_user()->id; - $result = db::build() - ->delete("twitter_tweets") - ->where("user_id", "=", $user_id) - ->where("item_id", "=", $item_id) - ->where("twitter_id", "=", "") - ->execute(); - } - } - /** * Get Twitter credentials for the current user. * @param int $user_id @@ -239,7 +221,11 @@ class Twitter_Controller extends Controller { */ private function _save_tweet($tweet) { if (!empty($tweet->item_id) && !empty($tweet->tweet)) { - $t = ORM::factory("twitter_tweet"); + if ($tweet->id > 0) { + $t = ORM::factory("twitter_tweet")->where("id", "=", $tweet->id)->find(); + } else { + $t = ORM::factory("twitter_tweet"); + } $t->item_id = $tweet->item_id; $t->twitter_id = $tweet->twitter_id; $t->tweet = $tweet->tweet; @@ -266,12 +252,4 @@ class Twitter_Controller extends Controller { message::success(t("Twitter access tokens saved!")); } - /** - * Update a previously failed tweet - * @param object $tweet - */ - private function _update_tweet($tweet) { - - } - } diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php index 59dc16c4..54df4e67 100644 --- a/3.1/modules/twitter/helpers/twitter.php +++ b/3.1/modules/twitter/helpers/twitter.php @@ -26,7 +26,6 @@ class twitter_Core { /** * Get module configure form * @return Forge - * @todo Set global Twitter account */ static function get_configure_form() { $form = new Forge("admin/twitter", "", "post", array("id" => "g-configure-twitter-form")); @@ -61,16 +60,17 @@ class twitter_Core { * Get tweet form * @param object $item * @return Forge - * @todo Load previously failed tweet for the current user for this item */ static function get_tweet_form($item) { $long_url = url::abs_site($item->relative_url_cache); // Check for saved tweets for this user and item - $saved_tweet = self::get_failed($item->id); - if ($saved_tweet) { - $default_tweet = $saved_tweet; + $saved = self::get_failed($item->id); + if ($saved) { + $default_tweet = $saved->tweet; + $tweet_id = $saved->id; } else { $default_tweet = module::get_var("twitter", "default_tweet"); + $tweet_id = 0; } $tweet = preg_replace("/%type/", $item->type, $default_tweet); $tweet = preg_replace("/%title/", $item->title, $tweet); @@ -90,6 +90,7 @@ class twitter_Core { ->rules("required") ->error_messages("required", t("Your tweet cannot be empty!")) ->id("g-tweet"); + $group->hidden("tweet_id")->value($tweet_id)->id("tweet_id"); $form->submit("")->value(t("Tweet")); return $form; } @@ -104,10 +105,10 @@ class twitter_Core { $t = ORM::factory("twitter_tweet") ->where("item_id", "=", $item_id) ->where("user_id", "=", $user_id) - ->where("twitter_id", "=", "") + ->where("twitter_id", "IS", NULL) ->find(); if ($t->loaded()) { - return $t->tweet; + return $t; } else { return false; } From e659f89527aea52c88107e72acb33ba29e9aa667 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 19:46:51 -0700 Subject: [PATCH 289/300] Moved twitteroauth library to vendor folder. Include this library's license file. Fixed path to "login with twitter" images. --- 3.1/modules/twitter/controllers/twitter.php | 22 +- .../twitter/vendor/twitteroauth/LICENSE | 22 + .../twitter/vendor/twitteroauth/OAuth.php | 874 ++++++++++++++++++ .../vendor/twitteroauth/images/darker.png | Bin 0 -> 2370 bytes .../vendor/twitteroauth/images/lighter.png | Bin 0 -> 2490 bytes .../vendor/twitteroauth/twitteroauth.php | 245 +++++ .../twitter/views/twitter_dialog.html.php | 2 +- 7 files changed, 1153 insertions(+), 12 deletions(-) create mode 100644 3.1/modules/twitter/vendor/twitteroauth/LICENSE create mode 100644 3.1/modules/twitter/vendor/twitteroauth/OAuth.php create mode 100644 3.1/modules/twitter/vendor/twitteroauth/images/darker.png create mode 100644 3.1/modules/twitter/vendor/twitteroauth/images/lighter.png create mode 100644 3.1/modules/twitter/vendor/twitteroauth/twitteroauth.php diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index 870df2b5..d9bc7a1f 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -24,7 +24,7 @@ class Twitter_Controller extends Controller { * Verify credentials and redirect based on response from Twitter. */ public function callback() { - require_once(MODPATH . "twitter/lib/twitteroauth.php"); + require_once(MODPATH . "twitter/vendor/twitteroauth/twitteroauth.php"); $consumer_key = module::get_var("twitter", "consumer_key"); $consumer_secret = module::get_var("twitter", "consumer_secret"); @@ -100,8 +100,8 @@ class Twitter_Controller extends Controller { * Redirect user to Twitter authorization page. */ public function redirect() { - require_once(MODPATH . "twitter/lib/twitteroauth.php"); - + require_once(MODPATH . "twitter/vendor/twitteroauth/twitteroauth.php"); + $consumer_key = module::get_var("twitter", "consumer_key"); $consumer_secret = module::get_var("twitter", "consumer_secret"); $oauth_callback = url::abs_site("twitter/callback"); @@ -110,13 +110,13 @@ class Twitter_Controller extends Controller { if (!empty($_GET['item_url'])) { Session::instance()->set("twitter_item_redirect", $_GET['item_url']); } - + // Build TwitterOAuth object with client credentials $connection = new TwitterOAuth($consumer_key, $consumer_secret); // Get temporary credentials. $request_token = $connection->getRequestToken($oauth_callback); - + // Save temporary credentials to session. Session::instance()->set("twitter_oauth_token", $request_token['oauth_token']); Session::instance()->set("twitter_oauth_token_secret", $request_token['oauth_token_secret']); @@ -140,24 +140,24 @@ class Twitter_Controller extends Controller { */ public function tweet($item_id) { access::verify_csrf(); - + $item = ORM::factory("item", $item_id); $form = twitter::get_tweet_form($item); - + if ($form->validate()) { $item_url = url::abs_site($item->relative_url_cache); $user = $this->_get_twitter_user(identity::active_user()->id); $consumer_key = module::get_var("twitter", "consumer_key"); $consumer_secret = module::get_var("twitter", "consumer_secret"); - require_once(MODPATH . "twitter/lib/twitteroauth.php"); - + require_once(MODPATH . "twitter/vendor/twitteroauth/twitteroauth.php"); + $connection = new TwitterOAuth( $consumer_key, $consumer_secret, $user->oauth_token, $user->oauth_token_secret); - + $message = $form->twitter_message->tweet->value; $response = $connection->post('statuses/update', array('status' => $message)); @@ -174,7 +174,7 @@ class Twitter_Controller extends Controller { $tweet->tweet = $message; $tweet->id = $form->twitter_message->tweet_id->value; $this->_save_tweet($tweet); - + } else { json::reply(array("result" => "error", "html" => (string)$form)); } diff --git a/3.1/modules/twitter/vendor/twitteroauth/LICENSE b/3.1/modules/twitter/vendor/twitteroauth/LICENSE new file mode 100644 index 00000000..233854f1 --- /dev/null +++ b/3.1/modules/twitter/vendor/twitteroauth/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2009 Abraham Williams - http://abrah.am - abraham@poseurte.ch + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/3.1/modules/twitter/vendor/twitteroauth/OAuth.php b/3.1/modules/twitter/vendor/twitteroauth/OAuth.php new file mode 100644 index 00000000..67a94c47 --- /dev/null +++ b/3.1/modules/twitter/vendor/twitteroauth/OAuth.php @@ -0,0 +1,874 @@ +key = $key; + $this->secret = $secret; + $this->callback_url = $callback_url; + } + + function __toString() { + return "OAuthConsumer[key=$this->key,secret=$this->secret]"; + } +} + +class OAuthToken { + // access tokens and request tokens + public $key; + public $secret; + + /** + * key = the token + * secret = the token secret + */ + function __construct($key, $secret) { + $this->key = $key; + $this->secret = $secret; + } + + /** + * generates the basic string serialization of a token that a server + * would respond to request_token and access_token calls with + */ + function to_string() { + return "oauth_token=" . + OAuthUtil::urlencode_rfc3986($this->key) . + "&oauth_token_secret=" . + OAuthUtil::urlencode_rfc3986($this->secret); + } + + function __toString() { + return $this->to_string(); + } +} + +/** + * A class for implementing a Signature Method + * See section 9 ("Signing Requests") in the spec + */ +abstract class OAuthSignatureMethod { + /** + * Needs to return the name of the Signature Method (ie HMAC-SHA1) + * @return string + */ + abstract public function get_name(); + + /** + * Build up the signature + * NOTE: The output of this function MUST NOT be urlencoded. + * the encoding is handled in OAuthRequest when the final + * request is serialized + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @return string + */ + abstract public function build_signature($request, $consumer, $token); + + /** + * Verifies that a given signature is correct + * @param OAuthRequest $request + * @param OAuthConsumer $consumer + * @param OAuthToken $token + * @param string $signature + * @return bool + */ + public function check_signature($request, $consumer, $token, $signature) { + $built = $this->build_signature($request, $consumer, $token); + return $built == $signature; + } +} + +/** + * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] + * where the Signature Base String is the text and the key is the concatenated values (each first + * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' + * character (ASCII code 38) even if empty. + * - Chapter 9.2 ("HMAC-SHA1") + */ +class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { + function get_name() { + return "HMAC-SHA1"; + } + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + + return base64_encode(hash_hmac('sha1', $base_string, $key, true)); + } +} + +/** + * The PLAINTEXT method does not provide any security protection and SHOULD only be used + * over a secure channel such as HTTPS. It does not use the Signature Base String. + * - Chapter 9.4 ("PLAINTEXT") + */ +class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { + public function get_name() { + return "PLAINTEXT"; + } + + /** + * oauth_signature is set to the concatenated encoded values of the Consumer Secret and + * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is + * empty. The result MUST be encoded again. + * - Chapter 9.4.1 ("Generating Signatures") + * + * Please note that the second encoding MUST NOT happen in the SignatureMethod, as + * OAuthRequest handles this! + */ + public function build_signature($request, $consumer, $token) { + $key_parts = array( + $consumer->secret, + ($token) ? $token->secret : "" + ); + + $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); + $key = implode('&', $key_parts); + $request->base_string = $key; + + return $key; + } +} + +/** + * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in + * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for + * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a + * verified way to the Service Provider, in a manner which is beyond the scope of this + * specification. + * - Chapter 9.3 ("RSA-SHA1") + */ +abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { + public function get_name() { + return "RSA-SHA1"; + } + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // (2) fetch via http using a url provided by the requester + // (3) some sort of specific discovery code based on request + // + // Either way should return a string representation of the certificate + protected abstract function fetch_public_cert(&$request); + + // Up to the SP to implement this lookup of keys. Possible ideas are: + // (1) do a lookup in a table of trusted certs keyed off of consumer + // + // Either way should return a string representation of the certificate + protected abstract function fetch_private_cert(&$request); + + public function build_signature($request, $consumer, $token) { + $base_string = $request->get_signature_base_string(); + $request->base_string = $base_string; + + // Fetch the private key cert based on the request + $cert = $this->fetch_private_cert($request); + + // Pull the private key ID from the certificate + $privatekeyid = openssl_get_privatekey($cert); + + // Sign using the key + $ok = openssl_sign($base_string, $signature, $privatekeyid); + + // Release the key resource + openssl_free_key($privatekeyid); + + return base64_encode($signature); + } + + public function check_signature($request, $consumer, $token, $signature) { + $decoded_sig = base64_decode($signature); + + $base_string = $request->get_signature_base_string(); + + // Fetch the public key cert based on the request + $cert = $this->fetch_public_cert($request); + + // Pull the public key ID from the certificate + $publickeyid = openssl_get_publickey($cert); + + // Check the computed signature against the one passed in the query + $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); + + // Release the key resource + openssl_free_key($publickeyid); + + return $ok == 1; + } +} + +class OAuthRequest { + private $parameters; + private $http_method; + private $http_url; + // for debug purposes + public $base_string; + public static $version = '1.0'; + public static $POST_INPUT = 'php://input'; + + function __construct($http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); + $this->parameters = $parameters; + $this->http_method = $http_method; + $this->http_url = $http_url; + } + + + /** + * attempt to build up a request from what was passed to the server + */ + public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { + $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") + ? 'http' + : 'https'; + @$http_url or $http_url = $scheme . + '://' . $_SERVER['HTTP_HOST'] . + ':' . + $_SERVER['SERVER_PORT'] . + $_SERVER['REQUEST_URI']; + @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; + + // We weren't handed any parameters, so let's find the ones relevant to + // this request. + // If you run XML-RPC or similar you should use this to provide your own + // parsed parameter-list + if (!$parameters) { + // Find request headers + $request_headers = OAuthUtil::get_headers(); + + // Parse the query-string to find GET parameters + $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); + + // It's a POST request of the proper content-type, so parse POST + // parameters and add those overriding any duplicates from GET + if ($http_method == "POST" + && @strstr($request_headers["Content-Type"], + "application/x-www-form-urlencoded") + ) { + $post_data = OAuthUtil::parse_parameters( + file_get_contents(self::$POST_INPUT) + ); + $parameters = array_merge($parameters, $post_data); + } + + // We have a Authorization-header with OAuth data. Parse the header + // and add those overriding any duplicates from GET or POST + if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { + $header_parameters = OAuthUtil::split_header( + $request_headers['Authorization'] + ); + $parameters = array_merge($parameters, $header_parameters); + } + + } + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + /** + * pretty much a helper function to set up the request + */ + public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { + @$parameters or $parameters = array(); + $defaults = array("oauth_version" => OAuthRequest::$version, + "oauth_nonce" => OAuthRequest::generate_nonce(), + "oauth_timestamp" => OAuthRequest::generate_timestamp(), + "oauth_consumer_key" => $consumer->key); + if ($token) + $defaults['oauth_token'] = $token->key; + + $parameters = array_merge($defaults, $parameters); + + return new OAuthRequest($http_method, $http_url, $parameters); + } + + public function set_parameter($name, $value, $allow_duplicates = true) { + if ($allow_duplicates && isset($this->parameters[$name])) { + // We have already added parameter(s) with this name, so add to the list + if (is_scalar($this->parameters[$name])) { + // This is the first duplicate, so transform scalar (string) + // into an array so we can add the duplicates + $this->parameters[$name] = array($this->parameters[$name]); + } + + $this->parameters[$name][] = $value; + } else { + $this->parameters[$name] = $value; + } + } + + public function get_parameter($name) { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + public function get_parameters() { + return $this->parameters; + } + + public function unset_parameter($name) { + unset($this->parameters[$name]); + } + + /** + * The request parameters, sorted and concatenated into a normalized string. + * @return string + */ + public function get_signable_parameters() { + // Grab all parameters + $params = $this->parameters; + + // Remove oauth_signature if present + // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") + if (isset($params['oauth_signature'])) { + unset($params['oauth_signature']); + } + + return OAuthUtil::build_http_query($params); + } + + /** + * Returns the base string of this request + * + * The base string defined as the method, the url + * and the parameters (normalized), each urlencoded + * and the concated with &. + */ + public function get_signature_base_string() { + $parts = array( + $this->get_normalized_http_method(), + $this->get_normalized_http_url(), + $this->get_signable_parameters() + ); + + $parts = OAuthUtil::urlencode_rfc3986($parts); + + return implode('&', $parts); + } + + /** + * just uppercases the http method + */ + public function get_normalized_http_method() { + return strtoupper($this->http_method); + } + + /** + * parses the url and rebuilds it to be + * scheme://host/path + */ + public function get_normalized_http_url() { + $parts = parse_url($this->http_url); + + $port = @$parts['port']; + $scheme = $parts['scheme']; + $host = $parts['host']; + $path = @$parts['path']; + + $port or $port = ($scheme == 'https') ? '443' : '80'; + + if (($scheme == 'https' && $port != '443') + || ($scheme == 'http' && $port != '80')) { + $host = "$host:$port"; + } + return "$scheme://$host$path"; + } + + /** + * builds a url usable for a GET request + */ + public function to_url() { + $post_data = $this->to_postdata(); + $out = $this->get_normalized_http_url(); + if ($post_data) { + $out .= '?'.$post_data; + } + return $out; + } + + /** + * builds the data one would send in a POST request + */ + public function to_postdata() { + return OAuthUtil::build_http_query($this->parameters); + } + + /** + * builds the Authorization: header + */ + public function to_header($realm=null) { + $first = true; + if($realm) { + $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; + $first = false; + } else + $out = 'Authorization: OAuth'; + + $total = array(); + foreach ($this->parameters as $k => $v) { + if (substr($k, 0, 5) != "oauth") continue; + if (is_array($v)) { + throw new OAuthException('Arrays not supported in headers'); + } + $out .= ($first) ? ' ' : ','; + $out .= OAuthUtil::urlencode_rfc3986($k) . + '="' . + OAuthUtil::urlencode_rfc3986($v) . + '"'; + $first = false; + } + return $out; + } + + public function __toString() { + return $this->to_url(); + } + + + public function sign_request($signature_method, $consumer, $token) { + $this->set_parameter( + "oauth_signature_method", + $signature_method->get_name(), + false + ); + $signature = $this->build_signature($signature_method, $consumer, $token); + $this->set_parameter("oauth_signature", $signature, false); + } + + public function build_signature($signature_method, $consumer, $token) { + $signature = $signature_method->build_signature($this, $consumer, $token); + return $signature; + } + + /** + * util function: current timestamp + */ + private static function generate_timestamp() { + return time(); + } + + /** + * util function: current nonce + */ + private static function generate_nonce() { + $mt = microtime(); + $rand = mt_rand(); + + return md5($mt . $rand); // md5s look nicer than numbers + } +} + +class OAuthServer { + protected $timestamp_threshold = 300; // in seconds, five minutes + protected $version = '1.0'; // hi blaine + protected $signature_methods = array(); + + protected $data_store; + + function __construct($data_store) { + $this->data_store = $data_store; + } + + public function add_signature_method($signature_method) { + $this->signature_methods[$signature_method->get_name()] = + $signature_method; + } + + // high level functions + + /** + * process a request_token request + * returns the request token on success + */ + public function fetch_request_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // no token required for the initial token request + $token = NULL; + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $callback = $request->get_parameter('oauth_callback'); + $new_token = $this->data_store->new_request_token($consumer, $callback); + + return $new_token; + } + + /** + * process an access_token request + * returns the access token on success + */ + public function fetch_access_token(&$request) { + $this->get_version($request); + + $consumer = $this->get_consumer($request); + + // requires authorized request token + $token = $this->get_token($request, $consumer, "request"); + + $this->check_signature($request, $consumer, $token); + + // Rev A change + $verifier = $request->get_parameter('oauth_verifier'); + $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); + + return $new_token; + } + + /** + * verify an api call, checks all the parameters + */ + public function verify_request(&$request) { + $this->get_version($request); + $consumer = $this->get_consumer($request); + $token = $this->get_token($request, $consumer, "access"); + $this->check_signature($request, $consumer, $token); + return array($consumer, $token); + } + + // Internals from here + /** + * version 1 + */ + private function get_version(&$request) { + $version = $request->get_parameter("oauth_version"); + if (!$version) { + // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. + // Chapter 7.0 ("Accessing Protected Ressources") + $version = '1.0'; + } + if ($version !== $this->version) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + /** + * figure out the signature with some defaults + */ + private function get_signature_method(&$request) { + $signature_method = + @$request->get_parameter("oauth_signature_method"); + + if (!$signature_method) { + // According to chapter 7 ("Accessing Protected Ressources") the signature-method + // parameter is required, and we can't just fallback to PLAINTEXT + throw new OAuthException('No signature method parameter. This parameter is required'); + } + + if (!in_array($signature_method, + array_keys($this->signature_methods))) { + throw new OAuthException( + "Signature method '$signature_method' not supported " . + "try one of the following: " . + implode(", ", array_keys($this->signature_methods)) + ); + } + return $this->signature_methods[$signature_method]; + } + + /** + * try to find the consumer for the provided request's consumer key + */ + private function get_consumer(&$request) { + $consumer_key = @$request->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $this->data_store->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + + return $consumer; + } + + /** + * try to find the token for the provided request's token key + */ + private function get_token(&$request, $consumer, $token_type="access") { + $token_field = @$request->get_parameter('oauth_token'); + $token = $this->data_store->lookup_token( + $consumer, $token_type, $token_field + ); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + /** + * all-in-one function to check the signature on a request + * should guess the signature method appropriately + */ + private function check_signature(&$request, $consumer, $token) { + // this should probably be in a different method + $timestamp = @$request->get_parameter('oauth_timestamp'); + $nonce = @$request->get_parameter('oauth_nonce'); + + $this->check_timestamp($timestamp); + $this->check_nonce($consumer, $token, $nonce, $timestamp); + + $signature_method = $this->get_signature_method($request); + + $signature = $request->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature( + $request, + $consumer, + $token, + $signature + ); + + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + /** + * check that the timestamp is new enough + */ + private function check_timestamp($timestamp) { + if( ! $timestamp ) + throw new OAuthException( + 'Missing timestamp parameter. The parameter is required' + ); + + // verify that timestamp is recentish + $now = time(); + if (abs($now - $timestamp) > $this->timestamp_threshold) { + throw new OAuthException( + "Expired timestamp, yours $timestamp, ours $now" + ); + } + } + + /** + * check that the nonce is not repeated + */ + private function check_nonce($consumer, $token, $nonce, $timestamp) { + if( ! $nonce ) + throw new OAuthException( + 'Missing nonce parameter. The parameter is required' + ); + + // verify that the nonce is uniqueish + $found = $this->data_store->lookup_nonce( + $consumer, + $token, + $nonce, + $timestamp + ); + if ($found) { + throw new OAuthException("Nonce already used: $nonce"); + } + } + +} + +class OAuthDataStore { + function lookup_consumer($consumer_key) { + // implement me + } + + function lookup_token($consumer, $token_type, $token) { + // implement me + } + + function lookup_nonce($consumer, $token, $nonce, $timestamp) { + // implement me + } + + function new_request_token($consumer, $callback = null) { + // return a new token attached to this consumer + } + + function new_access_token($token, $consumer, $verifier = null) { + // return a new access token attached to this consumer + // for the user associated with this token if the request token + // is authorized + // should also invalidate the request token + } + +} + +class OAuthUtil { + public static function urlencode_rfc3986($input) { + if (is_array($input)) { + return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); + } else if (is_scalar($input)) { + return str_replace( + '+', + ' ', + str_replace('%7E', '~', rawurlencode($input)) + ); + } else { + return ''; + } +} + + + // This decode function isn't taking into consideration the above + // modifications to the encoding process. However, this method doesn't + // seem to be used anywhere so leaving it as is. + public static function urldecode_rfc3986($string) { + return urldecode($string); + } + + // Utility function for turning the Authorization: header into + // parameters, has to do some unescaping + // Can filter out any non-oauth parameters if needed (default behaviour) + public static function split_header($header, $only_allow_oauth_parameters = true) { + $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; + $offset = 0; + $params = array(); + while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { + $match = $matches[0]; + $header_name = $matches[2][0]; + $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; + if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { + $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); + } + $offset = $match[1] + strlen($match[0]); + } + + if (isset($params['realm'])) { + unset($params['realm']); + } + + return $params; + } + + // helper to try to sort out headers for people who aren't running apache + public static function get_headers() { + if (function_exists('apache_request_headers')) { + // we need this to get the actual Authorization: header + // because apache tends to tell us it doesn't exist + $headers = apache_request_headers(); + + // sanitize the output of apache_request_headers because + // we always want the keys to be Cased-Like-This and arh() + // returns the headers in the same case as they are in the + // request + $out = array(); + foreach( $headers AS $key => $value ) { + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("-", " ", $key))) + ); + $out[$key] = $value; + } + } else { + // otherwise we don't have apache and are just going to have to hope + // that $_SERVER actually contains what we need + $out = array(); + if( isset($_SERVER['CONTENT_TYPE']) ) + $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; + if( isset($_ENV['CONTENT_TYPE']) ) + $out['Content-Type'] = $_ENV['CONTENT_TYPE']; + + foreach ($_SERVER as $key => $value) { + if (substr($key, 0, 5) == "HTTP_") { + // this is chaos, basically it is just there to capitalize the first + // letter of every word that is not an initial HTTP and strip HTTP + // code from przemek + $key = str_replace( + " ", + "-", + ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) + ); + $out[$key] = $value; + } + } + } + return $out; + } + + // This function takes a input like a=b&a=c&d=e and returns the parsed + // parameters like this + // array('a' => array('b','c'), 'd' => 'e') + public static function parse_parameters( $input ) { + if (!isset($input) || !$input) return array(); + + $pairs = explode('&', $input); + + $parsed_parameters = array(); + foreach ($pairs as $pair) { + $split = explode('=', $pair, 2); + $parameter = OAuthUtil::urldecode_rfc3986($split[0]); + $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; + + if (isset($parsed_parameters[$parameter])) { + // We have already recieved parameter(s) with this name, so add to the list + // of parameters with this name + + if (is_scalar($parsed_parameters[$parameter])) { + // This is the first duplicate, so transform scalar (string) into an array + // so we can add the duplicates + $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); + } + + $parsed_parameters[$parameter][] = $value; + } else { + $parsed_parameters[$parameter] = $value; + } + } + return $parsed_parameters; + } + + public static function build_http_query($params) { + if (!$params) return ''; + + // Urlencode both keys and values + $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); + $values = OAuthUtil::urlencode_rfc3986(array_values($params)); + $params = array_combine($keys, $values); + + // Parameters are sorted by name, using lexicographical byte value ordering. + // Ref: Spec: 9.1.1 (1) + uksort($params, 'strcmp'); + + $pairs = array(); + foreach ($params as $parameter => $value) { + if (is_array($value)) { + // If two or more parameters share the same name, they are sorted by their value + // Ref: Spec: 9.1.1 (1) + natsort($value); + foreach ($value as $duplicate_value) { + $pairs[] = $parameter . '=' . $duplicate_value; + } + } else { + $pairs[] = $parameter . '=' . $value; + } + } + // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) + // Each name-value pair is separated by an '&' character (ASCII code 38) + return implode('&', $pairs); + } +} + +?> diff --git a/3.1/modules/twitter/vendor/twitteroauth/images/darker.png b/3.1/modules/twitter/vendor/twitteroauth/images/darker.png new file mode 100644 index 0000000000000000000000000000000000000000..746b6b9f80c71049f2a4eb84ff72d5d1bf77c62c GIT binary patch literal 2370 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 literal 0 HcmV?d00001 diff --git a/3.1/modules/twitter/vendor/twitteroauth/images/lighter.png b/3.1/modules/twitter/vendor/twitteroauth/images/lighter.png new file mode 100644 index 0000000000000000000000000000000000000000..297bb03404f2d7462ee9355aae38f5f5f3e47fbd GIT binary patch literal 2490 zcmV;r2}SmaP)dbVG7wVRUJ4ZXi@?ZDjy8FEKeU zFgbGL({lg-0338hSaefwW^{L9a%BKPWN%_+AVz6&Wp{6KYjYq&Q#1y$)1UwV2@^>~ zK~!jg?VEdWl+_u=f8X9WA)72QVM!vF1VSK4AZQ?nK}o}NseS3JN$f zgRQMIo!W~OJ6`ZouvAJ_K&eH%O=zU35Dd9+OGJ}EvYTu++57IdU;kJVc9XpY6B?a; zX3k{ZIp=-eC+|7m`<-)kR{+>vQ7sWi9D|5ebEgWz_KIq$s4!>9f*d_|j-lj_V|e1| zv2!Q@e7>PCTvsDT9K#C=07>!<2 zJRT3v{^4m(e0B`AMuXes=AQD^+;C$tPj22q>4H*zdfz>`#KAid3>9xAeo+*W%jE#P zv+pgwyLbU*^X6iX@D|vxdJPV{BV^y=rMIzo>FtA}CzL%H7|@3PC-uR0k{o`MEpT&+ zmiCUwd$DjaD&G5mR^R=5Ugy>A zFL6`pd;osE=`rd~)-ZEc5shchvh}x5aZ~9nJpS;;UjCggyhMFX4IM@k^;K2OnpeX9 zJ+G59W-M>Ndx+<@J`*QB2?^MN1N-(fead7?=HEi6T!B@Box5IR=L;`>Ej{PX zHuTf??pp_V_K#2V&%f=WuDXUP)2>6K(eP;bS}ruV@b7*5sr;ydIrB8I2tIsDgsYdEB~aA*X6k7? zxX|2;qp^|Gwe@`Y#d&IupCC)EqUw`l7)GU&uTKu*U$o?QG#U*z%qZYj8`hDRoCLu8 zhbzgOI5DbLyT^;&?d{E@=<{Loc(HlBWQ@7?YwIarxgyB6oH@guS9eiXI-mWozJlq( z1uEY@fC2%N(MZ*iqs%Lw&3$(-XHs%f#Ci8GF3A_N^~J`q@N(Yec4#`>p8;Rm>`^(w z^()mX);zY6A1}KTOQ(hBpZ+tI2M_vvC6EgOasg^TuSW9u`2L~=xMTvlF=IdirA&q( z01}jz+(`M#6-+Id=Jypvl2wZSngV!x>{xvBuA7QRt)c4kYU--0D3~!Fhr>aA^+`_G z)iHDWG@POs#IMsQgCL+*sYp&si#>KieJ zAZPaXZl!3>9M-SjNX7dfvh?=bdf)$&1YWO*1jqy#ZFVPakM~lt{ns24pi(Mnce*e+ zoV|U5)IWA0uWtapa=C)>xf3{EU4zZ(WX19y(%RC@hySVMd_x1@*}REPr@KGD1PCBr zszd|P3jULJU(P|*6JUEpf1ZUYMN@b5y{gO~R0`R^ zzo2^1b1iYS>vBugsL9qPM&yVTyCwWp(Uo=ev@y*W7rS{ZmOePbh#j}_&X%YtxeZa(=9L5<&(e8Bi z<5wsYm^&^0MKr*8(egMLdIIf9`S}2Jn9Y=xl~T570XoAdF4!DlHH6a}anEJ-cv*VQ z<;7KRgYq-}Q)5qi@;qcECS38JtLDV#Y+3g(n;(3LUp}&dB%O}ct5)I-xFV2%>k11g zTUf@C&yKTa*KVdw^(R50loR*{_b2SKCHeZt4jkLpj^;ZlKOe6sQZ!>aT9uM(b0*;N zdYL_=0IS=B(gj7|CTgIo3w=s5DLNgUE;lDl z=6*ck(9~r1FRqNt5);TzP2p%4ylH~wfExv!+`pk8s}?!j(i+mgrP)GJVGd2L zrifxR3OQ=I9MLCX4Ray;#3LJ#2{HtkfT^vG#S0hk#JUIhwAC2&WDQrS)2nfk*V=iQ!Y2K(sVlqHpM%x^1{ck;y4Kin5jc`W;@8<8AFx4+YWs z?QSo2A}4+Q<6F?#y0F{rhttp_status; } + function lastAPICall() { return $this->last_api_call; } + + /** + * construct TwitterOAuth object + */ + function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { + $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); + if (!empty($oauth_token) && !empty($oauth_token_secret)) { + $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); + } else { + $this->token = NULL; + } + } + + + /** + * Get a request_token from Twitter + * + * @returns a key/value array containing oauth_token and oauth_token_secret + */ + function getRequestToken($oauth_callback = NULL) { + $parameters = array(); + if (!empty($oauth_callback)) { + $parameters['oauth_callback'] = $oauth_callback; + } + $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * Get the authorize URL + * + * @returns a string + */ + function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) { + if (is_array($token)) { + $token = $token['oauth_token']; + } + if (empty($sign_in_with_twitter)) { + return $this->authorizeURL() . "?oauth_token={$token}"; + } else { + return $this->authenticateURL() . "?oauth_token={$token}"; + } + } + + /** + * Exchange request token and secret for an access token and + * secret, to sign API calls. + * + * @returns array("oauth_token" => "the-access-token", + * "oauth_token_secret" => "the-access-secret", + * "user_id" => "9436992", + * "screen_name" => "abraham") + */ + function getAccessToken($oauth_verifier = FALSE) { + $parameters = array(); + if (!empty($oauth_verifier)) { + $parameters['oauth_verifier'] = $oauth_verifier; + } + $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * One time exchange of username and password for access token and secret. + * + * @returns array("oauth_token" => "the-access-token", + * "oauth_token_secret" => "the-access-secret", + * "user_id" => "9436992", + * "screen_name" => "abraham", + * "x_auth_expires" => "0") + */ + function getXAuthToken($username, $password) { + $parameters = array(); + $parameters['x_auth_username'] = $username; + $parameters['x_auth_password'] = $password; + $parameters['x_auth_mode'] = 'client_auth'; + $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); + $token = OAuthUtil::parse_parameters($request); + $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); + return $token; + } + + /** + * GET wrapper for oAuthRequest. + */ + function get($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'GET', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * POST wrapper for oAuthRequest. + */ + function post($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'POST', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * DELETE wrapper for oAuthReqeust. + */ + function delete($url, $parameters = array()) { + $response = $this->oAuthRequest($url, 'DELETE', $parameters); + if ($this->format === 'json' && $this->decode_json) { + return json_decode($response); + } + return $response; + } + + /** + * Format and sign an OAuth / API request + */ + function oAuthRequest($url, $method, $parameters) { + if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { + $url = "{$this->host}{$url}.{$this->format}"; + } + $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); + $request->sign_request($this->sha1_method, $this->consumer, $this->token); + switch ($method) { + case 'GET': + return $this->http($request->to_url(), 'GET'); + default: + return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); + } + } + + /** + * Make an HTTP request + * + * @return API results + */ + function http($url, $method, $postfields = NULL) { + $this->http_info = array(); + $ci = curl_init(); + /* Curl settings */ + curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); + curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); + curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); + curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:')); + curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); + curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); + curl_setopt($ci, CURLOPT_HEADER, FALSE); + + switch ($method) { + case 'POST': + curl_setopt($ci, CURLOPT_POST, TRUE); + if (!empty($postfields)) { + curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); + } + break; + case 'DELETE': + curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); + if (!empty($postfields)) { + $url = "{$url}?{$postfields}"; + } + } + + curl_setopt($ci, CURLOPT_URL, $url); + $response = curl_exec($ci); + $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); + $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); + $this->url = $url; + curl_close ($ci); + return $response; + } + + /** + * Get the header info to store. + */ + function getHeader($ch, $header) { + $i = strpos($header, ':'); + if (!empty($i)) { + $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); + $value = trim(substr($header, $i + 2)); + $this->http_header[$key] = $value; + } + return strlen($header); + } +} diff --git a/3.1/modules/twitter/views/twitter_dialog.html.php b/3.1/modules/twitter/views/twitter_dialog.html.php index a7cf91ba..751e5b20 100644 --- a/3.1/modules/twitter/views/twitter_dialog.html.php +++ b/3.1/modules/twitter/views/twitter_dialog.html.php @@ -10,7 +10,7 @@

    -

    Sign in with Twitter

    +

    " alt="Sign in with Twitter"/>

    From 2db277328ec1f32a840cc1eb2bdc4c4d92e7b7d7 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 19:49:18 -0700 Subject: [PATCH 290/300] Remove lib folder --- 3.1/modules/twitter/lib/OAuth.php | 874 --------------------- 3.1/modules/twitter/lib/images/darker.png | Bin 2370 -> 0 bytes 3.1/modules/twitter/lib/images/lighter.png | Bin 2490 -> 0 bytes 3.1/modules/twitter/lib/twitteroauth.php | 245 ------ 4 files changed, 1119 deletions(-) delete mode 100644 3.1/modules/twitter/lib/OAuth.php delete mode 100644 3.1/modules/twitter/lib/images/darker.png delete mode 100644 3.1/modules/twitter/lib/images/lighter.png delete mode 100644 3.1/modules/twitter/lib/twitteroauth.php diff --git a/3.1/modules/twitter/lib/OAuth.php b/3.1/modules/twitter/lib/OAuth.php deleted file mode 100644 index 67a94c47..00000000 --- a/3.1/modules/twitter/lib/OAuth.php +++ /dev/null @@ -1,874 +0,0 @@ -key = $key; - $this->secret = $secret; - $this->callback_url = $callback_url; - } - - function __toString() { - return "OAuthConsumer[key=$this->key,secret=$this->secret]"; - } -} - -class OAuthToken { - // access tokens and request tokens - public $key; - public $secret; - - /** - * key = the token - * secret = the token secret - */ - function __construct($key, $secret) { - $this->key = $key; - $this->secret = $secret; - } - - /** - * generates the basic string serialization of a token that a server - * would respond to request_token and access_token calls with - */ - function to_string() { - return "oauth_token=" . - OAuthUtil::urlencode_rfc3986($this->key) . - "&oauth_token_secret=" . - OAuthUtil::urlencode_rfc3986($this->secret); - } - - function __toString() { - return $this->to_string(); - } -} - -/** - * A class for implementing a Signature Method - * See section 9 ("Signing Requests") in the spec - */ -abstract class OAuthSignatureMethod { - /** - * Needs to return the name of the Signature Method (ie HMAC-SHA1) - * @return string - */ - abstract public function get_name(); - - /** - * Build up the signature - * NOTE: The output of this function MUST NOT be urlencoded. - * the encoding is handled in OAuthRequest when the final - * request is serialized - * @param OAuthRequest $request - * @param OAuthConsumer $consumer - * @param OAuthToken $token - * @return string - */ - abstract public function build_signature($request, $consumer, $token); - - /** - * Verifies that a given signature is correct - * @param OAuthRequest $request - * @param OAuthConsumer $consumer - * @param OAuthToken $token - * @param string $signature - * @return bool - */ - public function check_signature($request, $consumer, $token, $signature) { - $built = $this->build_signature($request, $consumer, $token); - return $built == $signature; - } -} - -/** - * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104] - * where the Signature Base String is the text and the key is the concatenated values (each first - * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&' - * character (ASCII code 38) even if empty. - * - Chapter 9.2 ("HMAC-SHA1") - */ -class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod { - function get_name() { - return "HMAC-SHA1"; - } - - public function build_signature($request, $consumer, $token) { - $base_string = $request->get_signature_base_string(); - $request->base_string = $base_string; - - $key_parts = array( - $consumer->secret, - ($token) ? $token->secret : "" - ); - - $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); - $key = implode('&', $key_parts); - - return base64_encode(hash_hmac('sha1', $base_string, $key, true)); - } -} - -/** - * The PLAINTEXT method does not provide any security protection and SHOULD only be used - * over a secure channel such as HTTPS. It does not use the Signature Base String. - * - Chapter 9.4 ("PLAINTEXT") - */ -class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod { - public function get_name() { - return "PLAINTEXT"; - } - - /** - * oauth_signature is set to the concatenated encoded values of the Consumer Secret and - * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is - * empty. The result MUST be encoded again. - * - Chapter 9.4.1 ("Generating Signatures") - * - * Please note that the second encoding MUST NOT happen in the SignatureMethod, as - * OAuthRequest handles this! - */ - public function build_signature($request, $consumer, $token) { - $key_parts = array( - $consumer->secret, - ($token) ? $token->secret : "" - ); - - $key_parts = OAuthUtil::urlencode_rfc3986($key_parts); - $key = implode('&', $key_parts); - $request->base_string = $key; - - return $key; - } -} - -/** - * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in - * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for - * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a - * verified way to the Service Provider, in a manner which is beyond the scope of this - * specification. - * - Chapter 9.3 ("RSA-SHA1") - */ -abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod { - public function get_name() { - return "RSA-SHA1"; - } - - // Up to the SP to implement this lookup of keys. Possible ideas are: - // (1) do a lookup in a table of trusted certs keyed off of consumer - // (2) fetch via http using a url provided by the requester - // (3) some sort of specific discovery code based on request - // - // Either way should return a string representation of the certificate - protected abstract function fetch_public_cert(&$request); - - // Up to the SP to implement this lookup of keys. Possible ideas are: - // (1) do a lookup in a table of trusted certs keyed off of consumer - // - // Either way should return a string representation of the certificate - protected abstract function fetch_private_cert(&$request); - - public function build_signature($request, $consumer, $token) { - $base_string = $request->get_signature_base_string(); - $request->base_string = $base_string; - - // Fetch the private key cert based on the request - $cert = $this->fetch_private_cert($request); - - // Pull the private key ID from the certificate - $privatekeyid = openssl_get_privatekey($cert); - - // Sign using the key - $ok = openssl_sign($base_string, $signature, $privatekeyid); - - // Release the key resource - openssl_free_key($privatekeyid); - - return base64_encode($signature); - } - - public function check_signature($request, $consumer, $token, $signature) { - $decoded_sig = base64_decode($signature); - - $base_string = $request->get_signature_base_string(); - - // Fetch the public key cert based on the request - $cert = $this->fetch_public_cert($request); - - // Pull the public key ID from the certificate - $publickeyid = openssl_get_publickey($cert); - - // Check the computed signature against the one passed in the query - $ok = openssl_verify($base_string, $decoded_sig, $publickeyid); - - // Release the key resource - openssl_free_key($publickeyid); - - return $ok == 1; - } -} - -class OAuthRequest { - private $parameters; - private $http_method; - private $http_url; - // for debug purposes - public $base_string; - public static $version = '1.0'; - public static $POST_INPUT = 'php://input'; - - function __construct($http_method, $http_url, $parameters=NULL) { - @$parameters or $parameters = array(); - $parameters = array_merge( OAuthUtil::parse_parameters(parse_url($http_url, PHP_URL_QUERY)), $parameters); - $this->parameters = $parameters; - $this->http_method = $http_method; - $this->http_url = $http_url; - } - - - /** - * attempt to build up a request from what was passed to the server - */ - public static function from_request($http_method=NULL, $http_url=NULL, $parameters=NULL) { - $scheme = (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on") - ? 'http' - : 'https'; - @$http_url or $http_url = $scheme . - '://' . $_SERVER['HTTP_HOST'] . - ':' . - $_SERVER['SERVER_PORT'] . - $_SERVER['REQUEST_URI']; - @$http_method or $http_method = $_SERVER['REQUEST_METHOD']; - - // We weren't handed any parameters, so let's find the ones relevant to - // this request. - // If you run XML-RPC or similar you should use this to provide your own - // parsed parameter-list - if (!$parameters) { - // Find request headers - $request_headers = OAuthUtil::get_headers(); - - // Parse the query-string to find GET parameters - $parameters = OAuthUtil::parse_parameters($_SERVER['QUERY_STRING']); - - // It's a POST request of the proper content-type, so parse POST - // parameters and add those overriding any duplicates from GET - if ($http_method == "POST" - && @strstr($request_headers["Content-Type"], - "application/x-www-form-urlencoded") - ) { - $post_data = OAuthUtil::parse_parameters( - file_get_contents(self::$POST_INPUT) - ); - $parameters = array_merge($parameters, $post_data); - } - - // We have a Authorization-header with OAuth data. Parse the header - // and add those overriding any duplicates from GET or POST - if (@substr($request_headers['Authorization'], 0, 6) == "OAuth ") { - $header_parameters = OAuthUtil::split_header( - $request_headers['Authorization'] - ); - $parameters = array_merge($parameters, $header_parameters); - } - - } - - return new OAuthRequest($http_method, $http_url, $parameters); - } - - /** - * pretty much a helper function to set up the request - */ - public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) { - @$parameters or $parameters = array(); - $defaults = array("oauth_version" => OAuthRequest::$version, - "oauth_nonce" => OAuthRequest::generate_nonce(), - "oauth_timestamp" => OAuthRequest::generate_timestamp(), - "oauth_consumer_key" => $consumer->key); - if ($token) - $defaults['oauth_token'] = $token->key; - - $parameters = array_merge($defaults, $parameters); - - return new OAuthRequest($http_method, $http_url, $parameters); - } - - public function set_parameter($name, $value, $allow_duplicates = true) { - if ($allow_duplicates && isset($this->parameters[$name])) { - // We have already added parameter(s) with this name, so add to the list - if (is_scalar($this->parameters[$name])) { - // This is the first duplicate, so transform scalar (string) - // into an array so we can add the duplicates - $this->parameters[$name] = array($this->parameters[$name]); - } - - $this->parameters[$name][] = $value; - } else { - $this->parameters[$name] = $value; - } - } - - public function get_parameter($name) { - return isset($this->parameters[$name]) ? $this->parameters[$name] : null; - } - - public function get_parameters() { - return $this->parameters; - } - - public function unset_parameter($name) { - unset($this->parameters[$name]); - } - - /** - * The request parameters, sorted and concatenated into a normalized string. - * @return string - */ - public function get_signable_parameters() { - // Grab all parameters - $params = $this->parameters; - - // Remove oauth_signature if present - // Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.") - if (isset($params['oauth_signature'])) { - unset($params['oauth_signature']); - } - - return OAuthUtil::build_http_query($params); - } - - /** - * Returns the base string of this request - * - * The base string defined as the method, the url - * and the parameters (normalized), each urlencoded - * and the concated with &. - */ - public function get_signature_base_string() { - $parts = array( - $this->get_normalized_http_method(), - $this->get_normalized_http_url(), - $this->get_signable_parameters() - ); - - $parts = OAuthUtil::urlencode_rfc3986($parts); - - return implode('&', $parts); - } - - /** - * just uppercases the http method - */ - public function get_normalized_http_method() { - return strtoupper($this->http_method); - } - - /** - * parses the url and rebuilds it to be - * scheme://host/path - */ - public function get_normalized_http_url() { - $parts = parse_url($this->http_url); - - $port = @$parts['port']; - $scheme = $parts['scheme']; - $host = $parts['host']; - $path = @$parts['path']; - - $port or $port = ($scheme == 'https') ? '443' : '80'; - - if (($scheme == 'https' && $port != '443') - || ($scheme == 'http' && $port != '80')) { - $host = "$host:$port"; - } - return "$scheme://$host$path"; - } - - /** - * builds a url usable for a GET request - */ - public function to_url() { - $post_data = $this->to_postdata(); - $out = $this->get_normalized_http_url(); - if ($post_data) { - $out .= '?'.$post_data; - } - return $out; - } - - /** - * builds the data one would send in a POST request - */ - public function to_postdata() { - return OAuthUtil::build_http_query($this->parameters); - } - - /** - * builds the Authorization: header - */ - public function to_header($realm=null) { - $first = true; - if($realm) { - $out = 'Authorization: OAuth realm="' . OAuthUtil::urlencode_rfc3986($realm) . '"'; - $first = false; - } else - $out = 'Authorization: OAuth'; - - $total = array(); - foreach ($this->parameters as $k => $v) { - if (substr($k, 0, 5) != "oauth") continue; - if (is_array($v)) { - throw new OAuthException('Arrays not supported in headers'); - } - $out .= ($first) ? ' ' : ','; - $out .= OAuthUtil::urlencode_rfc3986($k) . - '="' . - OAuthUtil::urlencode_rfc3986($v) . - '"'; - $first = false; - } - return $out; - } - - public function __toString() { - return $this->to_url(); - } - - - public function sign_request($signature_method, $consumer, $token) { - $this->set_parameter( - "oauth_signature_method", - $signature_method->get_name(), - false - ); - $signature = $this->build_signature($signature_method, $consumer, $token); - $this->set_parameter("oauth_signature", $signature, false); - } - - public function build_signature($signature_method, $consumer, $token) { - $signature = $signature_method->build_signature($this, $consumer, $token); - return $signature; - } - - /** - * util function: current timestamp - */ - private static function generate_timestamp() { - return time(); - } - - /** - * util function: current nonce - */ - private static function generate_nonce() { - $mt = microtime(); - $rand = mt_rand(); - - return md5($mt . $rand); // md5s look nicer than numbers - } -} - -class OAuthServer { - protected $timestamp_threshold = 300; // in seconds, five minutes - protected $version = '1.0'; // hi blaine - protected $signature_methods = array(); - - protected $data_store; - - function __construct($data_store) { - $this->data_store = $data_store; - } - - public function add_signature_method($signature_method) { - $this->signature_methods[$signature_method->get_name()] = - $signature_method; - } - - // high level functions - - /** - * process a request_token request - * returns the request token on success - */ - public function fetch_request_token(&$request) { - $this->get_version($request); - - $consumer = $this->get_consumer($request); - - // no token required for the initial token request - $token = NULL; - - $this->check_signature($request, $consumer, $token); - - // Rev A change - $callback = $request->get_parameter('oauth_callback'); - $new_token = $this->data_store->new_request_token($consumer, $callback); - - return $new_token; - } - - /** - * process an access_token request - * returns the access token on success - */ - public function fetch_access_token(&$request) { - $this->get_version($request); - - $consumer = $this->get_consumer($request); - - // requires authorized request token - $token = $this->get_token($request, $consumer, "request"); - - $this->check_signature($request, $consumer, $token); - - // Rev A change - $verifier = $request->get_parameter('oauth_verifier'); - $new_token = $this->data_store->new_access_token($token, $consumer, $verifier); - - return $new_token; - } - - /** - * verify an api call, checks all the parameters - */ - public function verify_request(&$request) { - $this->get_version($request); - $consumer = $this->get_consumer($request); - $token = $this->get_token($request, $consumer, "access"); - $this->check_signature($request, $consumer, $token); - return array($consumer, $token); - } - - // Internals from here - /** - * version 1 - */ - private function get_version(&$request) { - $version = $request->get_parameter("oauth_version"); - if (!$version) { - // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present. - // Chapter 7.0 ("Accessing Protected Ressources") - $version = '1.0'; - } - if ($version !== $this->version) { - throw new OAuthException("OAuth version '$version' not supported"); - } - return $version; - } - - /** - * figure out the signature with some defaults - */ - private function get_signature_method(&$request) { - $signature_method = - @$request->get_parameter("oauth_signature_method"); - - if (!$signature_method) { - // According to chapter 7 ("Accessing Protected Ressources") the signature-method - // parameter is required, and we can't just fallback to PLAINTEXT - throw new OAuthException('No signature method parameter. This parameter is required'); - } - - if (!in_array($signature_method, - array_keys($this->signature_methods))) { - throw new OAuthException( - "Signature method '$signature_method' not supported " . - "try one of the following: " . - implode(", ", array_keys($this->signature_methods)) - ); - } - return $this->signature_methods[$signature_method]; - } - - /** - * try to find the consumer for the provided request's consumer key - */ - private function get_consumer(&$request) { - $consumer_key = @$request->get_parameter("oauth_consumer_key"); - if (!$consumer_key) { - throw new OAuthException("Invalid consumer key"); - } - - $consumer = $this->data_store->lookup_consumer($consumer_key); - if (!$consumer) { - throw new OAuthException("Invalid consumer"); - } - - return $consumer; - } - - /** - * try to find the token for the provided request's token key - */ - private function get_token(&$request, $consumer, $token_type="access") { - $token_field = @$request->get_parameter('oauth_token'); - $token = $this->data_store->lookup_token( - $consumer, $token_type, $token_field - ); - if (!$token) { - throw new OAuthException("Invalid $token_type token: $token_field"); - } - return $token; - } - - /** - * all-in-one function to check the signature on a request - * should guess the signature method appropriately - */ - private function check_signature(&$request, $consumer, $token) { - // this should probably be in a different method - $timestamp = @$request->get_parameter('oauth_timestamp'); - $nonce = @$request->get_parameter('oauth_nonce'); - - $this->check_timestamp($timestamp); - $this->check_nonce($consumer, $token, $nonce, $timestamp); - - $signature_method = $this->get_signature_method($request); - - $signature = $request->get_parameter('oauth_signature'); - $valid_sig = $signature_method->check_signature( - $request, - $consumer, - $token, - $signature - ); - - if (!$valid_sig) { - throw new OAuthException("Invalid signature"); - } - } - - /** - * check that the timestamp is new enough - */ - private function check_timestamp($timestamp) { - if( ! $timestamp ) - throw new OAuthException( - 'Missing timestamp parameter. The parameter is required' - ); - - // verify that timestamp is recentish - $now = time(); - if (abs($now - $timestamp) > $this->timestamp_threshold) { - throw new OAuthException( - "Expired timestamp, yours $timestamp, ours $now" - ); - } - } - - /** - * check that the nonce is not repeated - */ - private function check_nonce($consumer, $token, $nonce, $timestamp) { - if( ! $nonce ) - throw new OAuthException( - 'Missing nonce parameter. The parameter is required' - ); - - // verify that the nonce is uniqueish - $found = $this->data_store->lookup_nonce( - $consumer, - $token, - $nonce, - $timestamp - ); - if ($found) { - throw new OAuthException("Nonce already used: $nonce"); - } - } - -} - -class OAuthDataStore { - function lookup_consumer($consumer_key) { - // implement me - } - - function lookup_token($consumer, $token_type, $token) { - // implement me - } - - function lookup_nonce($consumer, $token, $nonce, $timestamp) { - // implement me - } - - function new_request_token($consumer, $callback = null) { - // return a new token attached to this consumer - } - - function new_access_token($token, $consumer, $verifier = null) { - // return a new access token attached to this consumer - // for the user associated with this token if the request token - // is authorized - // should also invalidate the request token - } - -} - -class OAuthUtil { - public static function urlencode_rfc3986($input) { - if (is_array($input)) { - return array_map(array('OAuthUtil', 'urlencode_rfc3986'), $input); - } else if (is_scalar($input)) { - return str_replace( - '+', - ' ', - str_replace('%7E', '~', rawurlencode($input)) - ); - } else { - return ''; - } -} - - - // This decode function isn't taking into consideration the above - // modifications to the encoding process. However, this method doesn't - // seem to be used anywhere so leaving it as is. - public static function urldecode_rfc3986($string) { - return urldecode($string); - } - - // Utility function for turning the Authorization: header into - // parameters, has to do some unescaping - // Can filter out any non-oauth parameters if needed (default behaviour) - public static function split_header($header, $only_allow_oauth_parameters = true) { - $pattern = '/(([-_a-z]*)=("([^"]*)"|([^,]*)),?)/'; - $offset = 0; - $params = array(); - while (preg_match($pattern, $header, $matches, PREG_OFFSET_CAPTURE, $offset) > 0) { - $match = $matches[0]; - $header_name = $matches[2][0]; - $header_content = (isset($matches[5])) ? $matches[5][0] : $matches[4][0]; - if (preg_match('/^oauth_/', $header_name) || !$only_allow_oauth_parameters) { - $params[$header_name] = OAuthUtil::urldecode_rfc3986($header_content); - } - $offset = $match[1] + strlen($match[0]); - } - - if (isset($params['realm'])) { - unset($params['realm']); - } - - return $params; - } - - // helper to try to sort out headers for people who aren't running apache - public static function get_headers() { - if (function_exists('apache_request_headers')) { - // we need this to get the actual Authorization: header - // because apache tends to tell us it doesn't exist - $headers = apache_request_headers(); - - // sanitize the output of apache_request_headers because - // we always want the keys to be Cased-Like-This and arh() - // returns the headers in the same case as they are in the - // request - $out = array(); - foreach( $headers AS $key => $value ) { - $key = str_replace( - " ", - "-", - ucwords(strtolower(str_replace("-", " ", $key))) - ); - $out[$key] = $value; - } - } else { - // otherwise we don't have apache and are just going to have to hope - // that $_SERVER actually contains what we need - $out = array(); - if( isset($_SERVER['CONTENT_TYPE']) ) - $out['Content-Type'] = $_SERVER['CONTENT_TYPE']; - if( isset($_ENV['CONTENT_TYPE']) ) - $out['Content-Type'] = $_ENV['CONTENT_TYPE']; - - foreach ($_SERVER as $key => $value) { - if (substr($key, 0, 5) == "HTTP_") { - // this is chaos, basically it is just there to capitalize the first - // letter of every word that is not an initial HTTP and strip HTTP - // code from przemek - $key = str_replace( - " ", - "-", - ucwords(strtolower(str_replace("_", " ", substr($key, 5)))) - ); - $out[$key] = $value; - } - } - } - return $out; - } - - // This function takes a input like a=b&a=c&d=e and returns the parsed - // parameters like this - // array('a' => array('b','c'), 'd' => 'e') - public static function parse_parameters( $input ) { - if (!isset($input) || !$input) return array(); - - $pairs = explode('&', $input); - - $parsed_parameters = array(); - foreach ($pairs as $pair) { - $split = explode('=', $pair, 2); - $parameter = OAuthUtil::urldecode_rfc3986($split[0]); - $value = isset($split[1]) ? OAuthUtil::urldecode_rfc3986($split[1]) : ''; - - if (isset($parsed_parameters[$parameter])) { - // We have already recieved parameter(s) with this name, so add to the list - // of parameters with this name - - if (is_scalar($parsed_parameters[$parameter])) { - // This is the first duplicate, so transform scalar (string) into an array - // so we can add the duplicates - $parsed_parameters[$parameter] = array($parsed_parameters[$parameter]); - } - - $parsed_parameters[$parameter][] = $value; - } else { - $parsed_parameters[$parameter] = $value; - } - } - return $parsed_parameters; - } - - public static function build_http_query($params) { - if (!$params) return ''; - - // Urlencode both keys and values - $keys = OAuthUtil::urlencode_rfc3986(array_keys($params)); - $values = OAuthUtil::urlencode_rfc3986(array_values($params)); - $params = array_combine($keys, $values); - - // Parameters are sorted by name, using lexicographical byte value ordering. - // Ref: Spec: 9.1.1 (1) - uksort($params, 'strcmp'); - - $pairs = array(); - foreach ($params as $parameter => $value) { - if (is_array($value)) { - // If two or more parameters share the same name, they are sorted by their value - // Ref: Spec: 9.1.1 (1) - natsort($value); - foreach ($value as $duplicate_value) { - $pairs[] = $parameter . '=' . $duplicate_value; - } - } else { - $pairs[] = $parameter . '=' . $value; - } - } - // For each parameter, the name is separated from the corresponding value by an '=' character (ASCII code 61) - // Each name-value pair is separated by an '&' character (ASCII code 38) - return implode('&', $pairs); - } -} - -?> diff --git a/3.1/modules/twitter/lib/images/darker.png b/3.1/modules/twitter/lib/images/darker.png deleted file mode 100644 index 746b6b9f80c71049f2a4eb84ff72d5d1bf77c62c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2370 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 diff --git a/3.1/modules/twitter/lib/images/lighter.png b/3.1/modules/twitter/lib/images/lighter.png deleted file mode 100644 index 297bb03404f2d7462ee9355aae38f5f5f3e47fbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2490 zcmV;r2}SmaP)dbVG7wVRUJ4ZXi@?ZDjy8FEKeU zFgbGL({lg-0338hSaefwW^{L9a%BKPWN%_+AVz6&Wp{6KYjYq&Q#1y$)1UwV2@^>~ zK~!jg?VEdWl+_u=f8X9WA)72QVM!vF1VSK4AZQ?nK}o}NseS3JN$f zgRQMIo!W~OJ6`ZouvAJ_K&eH%O=zU35Dd9+OGJ}EvYTu++57IdU;kJVc9XpY6B?a; zX3k{ZIp=-eC+|7m`<-)kR{+>vQ7sWi9D|5ebEgWz_KIq$s4!>9f*d_|j-lj_V|e1| zv2!Q@e7>PCTvsDT9K#C=07>!<2 zJRT3v{^4m(e0B`AMuXes=AQD^+;C$tPj22q>4H*zdfz>`#KAid3>9xAeo+*W%jE#P zv+pgwyLbU*^X6iX@D|vxdJPV{BV^y=rMIzo>FtA}CzL%H7|@3PC-uR0k{o`MEpT&+ zmiCUwd$DjaD&G5mR^R=5Ugy>A zFL6`pd;osE=`rd~)-ZEc5shchvh}x5aZ~9nJpS;;UjCggyhMFX4IM@k^;K2OnpeX9 zJ+G59W-M>Ndx+<@J`*QB2?^MN1N-(fead7?=HEi6T!B@Box5IR=L;`>Ej{PX zHuTf??pp_V_K#2V&%f=WuDXUP)2>6K(eP;bS}ruV@b7*5sr;ydIrB8I2tIsDgsYdEB~aA*X6k7? zxX|2;qp^|Gwe@`Y#d&IupCC)EqUw`l7)GU&uTKu*U$o?QG#U*z%qZYj8`hDRoCLu8 zhbzgOI5DbLyT^;&?d{E@=<{Loc(HlBWQ@7?YwIarxgyB6oH@guS9eiXI-mWozJlq( z1uEY@fC2%N(MZ*iqs%Lw&3$(-XHs%f#Ci8GF3A_N^~J`q@N(Yec4#`>p8;Rm>`^(w z^()mX);zY6A1}KTOQ(hBpZ+tI2M_vvC6EgOasg^TuSW9u`2L~=xMTvlF=IdirA&q( z01}jz+(`M#6-+Id=Jypvl2wZSngV!x>{xvBuA7QRt)c4kYU--0D3~!Fhr>aA^+`_G z)iHDWG@POs#IMsQgCL+*sYp&si#>KieJ zAZPaXZl!3>9M-SjNX7dfvh?=bdf)$&1YWO*1jqy#ZFVPakM~lt{ns24pi(Mnce*e+ zoV|U5)IWA0uWtapa=C)>xf3{EU4zZ(WX19y(%RC@hySVMd_x1@*}REPr@KGD1PCBr zszd|P3jULJU(P|*6JUEpf1ZUYMN@b5y{gO~R0`R^ zzo2^1b1iYS>vBugsL9qPM&yVTyCwWp(Uo=ev@y*W7rS{ZmOePbh#j}_&X%YtxeZa(=9L5<&(e8Bi z<5wsYm^&^0MKr*8(egMLdIIf9`S}2Jn9Y=xl~T570XoAdF4!DlHH6a}anEJ-cv*VQ z<;7KRgYq-}Q)5qi@;qcECS38JtLDV#Y+3g(n;(3LUp}&dB%O}ct5)I-xFV2%>k11g zTUf@C&yKTa*KVdw^(R50loR*{_b2SKCHeZt4jkLpj^;ZlKOe6sQZ!>aT9uM(b0*;N zdYL_=0IS=B(gj7|CTgIo3w=s5DLNgUE;lDl z=6*ck(9~r1FRqNt5);TzP2p%4ylH~wfExv!+`pk8s}?!j(i+mgrP)GJVGd2L zrifxR3OQ=I9MLCX4Ray;#3LJ#2{HtkfT^vG#S0hk#JUIhwAC2&WDQrS)2nfk*V=iQ!Y2K(sVlqHpM%x^1{ck;y4Kin5jc`W;@8<8AFx4+YWs z?QSo2A}4+Q<6F?#y0F{rhttp_status; } - function lastAPICall() { return $this->last_api_call; } - - /** - * construct TwitterOAuth object - */ - function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) { - $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1(); - $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); - if (!empty($oauth_token) && !empty($oauth_token_secret)) { - $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret); - } else { - $this->token = NULL; - } - } - - - /** - * Get a request_token from Twitter - * - * @returns a key/value array containing oauth_token and oauth_token_secret - */ - function getRequestToken($oauth_callback = NULL) { - $parameters = array(); - if (!empty($oauth_callback)) { - $parameters['oauth_callback'] = $oauth_callback; - } - $request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters); - $token = OAuthUtil::parse_parameters($request); - $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); - return $token; - } - - /** - * Get the authorize URL - * - * @returns a string - */ - function getAuthorizeURL($token, $sign_in_with_twitter = TRUE) { - if (is_array($token)) { - $token = $token['oauth_token']; - } - if (empty($sign_in_with_twitter)) { - return $this->authorizeURL() . "?oauth_token={$token}"; - } else { - return $this->authenticateURL() . "?oauth_token={$token}"; - } - } - - /** - * Exchange request token and secret for an access token and - * secret, to sign API calls. - * - * @returns array("oauth_token" => "the-access-token", - * "oauth_token_secret" => "the-access-secret", - * "user_id" => "9436992", - * "screen_name" => "abraham") - */ - function getAccessToken($oauth_verifier = FALSE) { - $parameters = array(); - if (!empty($oauth_verifier)) { - $parameters['oauth_verifier'] = $oauth_verifier; - } - $request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters); - $token = OAuthUtil::parse_parameters($request); - $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); - return $token; - } - - /** - * One time exchange of username and password for access token and secret. - * - * @returns array("oauth_token" => "the-access-token", - * "oauth_token_secret" => "the-access-secret", - * "user_id" => "9436992", - * "screen_name" => "abraham", - * "x_auth_expires" => "0") - */ - function getXAuthToken($username, $password) { - $parameters = array(); - $parameters['x_auth_username'] = $username; - $parameters['x_auth_password'] = $password; - $parameters['x_auth_mode'] = 'client_auth'; - $request = $this->oAuthRequest($this->accessTokenURL(), 'POST', $parameters); - $token = OAuthUtil::parse_parameters($request); - $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']); - return $token; - } - - /** - * GET wrapper for oAuthRequest. - */ - function get($url, $parameters = array()) { - $response = $this->oAuthRequest($url, 'GET', $parameters); - if ($this->format === 'json' && $this->decode_json) { - return json_decode($response); - } - return $response; - } - - /** - * POST wrapper for oAuthRequest. - */ - function post($url, $parameters = array()) { - $response = $this->oAuthRequest($url, 'POST', $parameters); - if ($this->format === 'json' && $this->decode_json) { - return json_decode($response); - } - return $response; - } - - /** - * DELETE wrapper for oAuthReqeust. - */ - function delete($url, $parameters = array()) { - $response = $this->oAuthRequest($url, 'DELETE', $parameters); - if ($this->format === 'json' && $this->decode_json) { - return json_decode($response); - } - return $response; - } - - /** - * Format and sign an OAuth / API request - */ - function oAuthRequest($url, $method, $parameters) { - if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) { - $url = "{$this->host}{$url}.{$this->format}"; - } - $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters); - $request->sign_request($this->sha1_method, $this->consumer, $this->token); - switch ($method) { - case 'GET': - return $this->http($request->to_url(), 'GET'); - default: - return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata()); - } - } - - /** - * Make an HTTP request - * - * @return API results - */ - function http($url, $method, $postfields = NULL) { - $this->http_info = array(); - $ci = curl_init(); - /* Curl settings */ - curl_setopt($ci, CURLOPT_USERAGENT, $this->useragent); - curl_setopt($ci, CURLOPT_CONNECTTIMEOUT, $this->connecttimeout); - curl_setopt($ci, CURLOPT_TIMEOUT, $this->timeout); - curl_setopt($ci, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($ci, CURLOPT_HTTPHEADER, array('Expect:')); - curl_setopt($ci, CURLOPT_SSL_VERIFYPEER, $this->ssl_verifypeer); - curl_setopt($ci, CURLOPT_HEADERFUNCTION, array($this, 'getHeader')); - curl_setopt($ci, CURLOPT_HEADER, FALSE); - - switch ($method) { - case 'POST': - curl_setopt($ci, CURLOPT_POST, TRUE); - if (!empty($postfields)) { - curl_setopt($ci, CURLOPT_POSTFIELDS, $postfields); - } - break; - case 'DELETE': - curl_setopt($ci, CURLOPT_CUSTOMREQUEST, 'DELETE'); - if (!empty($postfields)) { - $url = "{$url}?{$postfields}"; - } - } - - curl_setopt($ci, CURLOPT_URL, $url); - $response = curl_exec($ci); - $this->http_code = curl_getinfo($ci, CURLINFO_HTTP_CODE); - $this->http_info = array_merge($this->http_info, curl_getinfo($ci)); - $this->url = $url; - curl_close ($ci); - return $response; - } - - /** - * Get the header info to store. - */ - function getHeader($ch, $header) { - $i = strpos($header, ':'); - if (!empty($i)) { - $key = str_replace('-', '_', strtolower(substr($header, 0, $i))); - $value = trim(substr($header, $i + 2)); - $this->http_header[$key] = $value; - } - return strlen($header); - } -} From 8d613c1b9d1c0afb806cba1861e52b47bbec8687 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Sun, 13 Feb 2011 22:35:15 -0700 Subject: [PATCH 291/300] Correct error message display and redirect. --- 3.1/modules/twitter/controllers/twitter.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index d9bc7a1f..c29efedf 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -136,7 +136,6 @@ class Twitter_Controller extends Controller { /** * Post a status update to Twitter * @param int $item_id - * @todo Display Twitter API errors in Tweet dialog */ public function tweet($item_id) { access::verify_csrf(); @@ -165,9 +164,10 @@ class Twitter_Controller extends Controller { message::success(t("Tweet sent!")); json::reply(array("result" => "success", "location" => $item->url())); } else { - log::error("content", "Twitter", "Unable to send tweet: " . $connection->http_code); - message::error(t("Unable to send Tweet. Your message has been saved. Please try again later.")); - json::reply(array("result" => "error", "html" => (string)$form)); + message::error(t("Unable to send, your Tweet has been saved. Please try again later.")); + log::error("content", "Twitter", t("Unable to send tweet: %http_code", + array("http_code" => $connection->http_code))); + json::reply(array("result" => "success", "location" => $item->url())); } $tweet->item_id = $item_id; (!empty($response->id)) ? $tweet->twitter_id = $response->id : $tweet->twitter_id = NULL; From 1fc1a27364dd62d43d8542f526ae2ca0b068f105 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 17:19:25 -0700 Subject: [PATCH 292/300] Re-added user_id to CREATE TABLE in the installer helper. --- 3.1/modules/twitter/helpers/twitter_installer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/3.1/modules/twitter/helpers/twitter_installer.php b/3.1/modules/twitter/helpers/twitter_installer.php index 4e256b64..58016a08 100644 --- a/3.1/modules/twitter/helpers/twitter_installer.php +++ b/3.1/modules/twitter/helpers/twitter_installer.php @@ -24,10 +24,10 @@ class twitter_installer { ->query("CREATE TABLE {twitter_tweets} ( `id` int(9) NOT NULL AUTO_INCREMENT, `item_id` int(9) NOT NULL, + `sent` int(9) NULL, `twitter_id` decimal(20,0) NULL, `tweet` varchar(255) NOT NULL, - `sent` int(9) NULL, - `created` int(9) NOT NULL, + `user_id` int(9) NOT NULL, PRIMARY KEY (`id`)) DEFAULT CHARSET=utf8;"); Database::instance() From c5d19be54bf930a924a3037ce24f348cf77b79f8 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 17:24:55 -0700 Subject: [PATCH 293/300] Use url::file() instead of url::site() when referencing images. --- 3.1/modules/twitter/views/twitter_dialog.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/twitter/views/twitter_dialog.html.php b/3.1/modules/twitter/views/twitter_dialog.html.php index 751e5b20..b916de2e 100644 --- a/3.1/modules/twitter/views/twitter_dialog.html.php +++ b/3.1/modules/twitter/views/twitter_dialog.html.php @@ -10,7 +10,7 @@

    -

    " alt="Sign in with Twitter"/>

    +

    " alt="Sign in with Twitter"/>

    From 6fb1e21416dfa1542ee6308efe707efecb306796 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 19:24:19 -0700 Subject: [PATCH 294/300] Don't show 'Share on Twitter' link if the user's not logged in. --- 3.1/modules/twitter/helpers/twitter_event.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/3.1/modules/twitter/helpers/twitter_event.php b/3.1/modules/twitter/helpers/twitter_event.php index 37c923a9..5dc810d6 100644 --- a/3.1/modules/twitter/helpers/twitter_event.php +++ b/3.1/modules/twitter/helpers/twitter_event.php @@ -28,8 +28,8 @@ class twitter_event_Core { } static function site_menu($menu, $theme) { - $item = $theme->item(); - if (twitter::is_registered()) { + if ((identity::active_user()->id > 1) && twitter::is_registered()) { + $item = $theme->item(); $menu->get("options_menu") ->append(Menu::factory("dialog") ->id("twitter") @@ -40,7 +40,7 @@ class twitter_event_Core { } static function context_menu($menu, $theme, $item) { - if (twitter::is_registered()) { + if ((identity::active_user()->id > 1) && twitter::is_registered()) { $menu->get("options_menu") ->append(Menu::factory("dialog") ->id("twitter") From cb9cf301331280c86c5fa9f8b021715149176768 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 20:17:53 -0700 Subject: [PATCH 295/300] Fix Twitter OAuth redirect, update access token saved confirmation message. --- 3.1/modules/twitter/controllers/twitter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/3.1/modules/twitter/controllers/twitter.php b/3.1/modules/twitter/controllers/twitter.php index c29efedf..ecd195d6 100644 --- a/3.1/modules/twitter/controllers/twitter.php +++ b/3.1/modules/twitter/controllers/twitter.php @@ -125,11 +125,11 @@ class Twitter_Controller extends Controller { if (200 == $connection->http_code) { // Build authorize URL and redirect user to Twitter $url = $connection->getAuthorizeURL($request_token["oauth_token"]); - url::redirect(url::site($url)); + url::redirect($url); } else { // Show notification if something went wrong message::success(t("Could not connect to Twitter. Refresh the page or try again later.")); - url::redirect(url::site($url)); + url::redirect(urldecode($_GET['item_url'])); } } @@ -249,7 +249,7 @@ class Twitter_Controller extends Controller { $u->user_id = identity::active_user()->id; $u->save(); - message::success(t("Twitter access tokens saved!")); + message::success(t("Success! You may now share Gallery items on Twitter.")); } } From 94e0758c27065c5d9391c098455545144f1b0cee Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 20:43:47 -0700 Subject: [PATCH 296/300] Updated authorize notice message. --- 3.1/modules/twitter/views/twitter_dialog.html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3.1/modules/twitter/views/twitter_dialog.html.php b/3.1/modules/twitter/views/twitter_dialog.html.php index b916de2e..b39c7928 100644 --- a/3.1/modules/twitter/views/twitter_dialog.html.php +++ b/3.1/modules/twitter/views/twitter_dialog.html.php @@ -9,7 +9,7 @@

    -

    +

    " alt="Sign in with Twitter"/>

    From eddddf04b141b39b60fcaba5074978634f084bfa Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 22:40:08 -0700 Subject: [PATCH 297/300] Restored reset_default_tweet(), without it the default tweet value isn't set during installation. --- 3.1/modules/twitter/controllers/admin_twitter.php | 9 +-------- 3.1/modules/twitter/helpers/twitter.php | 4 ++++ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/3.1/modules/twitter/controllers/admin_twitter.php b/3.1/modules/twitter/controllers/admin_twitter.php index a65168fb..5c954953 100644 --- a/3.1/modules/twitter/controllers/admin_twitter.php +++ b/3.1/modules/twitter/controllers/admin_twitter.php @@ -19,13 +19,6 @@ */ class Admin_Twitter_Controller extends Admin_Controller { - public $default_tweet; - - function __construct() { - parent::__construct(); - $this->default_tweet = t("Check out this %type, '%title': %description %url"); - } - /** * bit.ly module's settings * @todo Show default tweet value after resetting it! @@ -40,7 +33,7 @@ class Admin_Twitter_Controller extends Admin_Controller { $reset_tweet = $form->twitter_message->reset_tweet->value; if ($reset_tweet) { $default_tweet = $this->default_tweet; - $form->twitter_message->default_tweet->value = $this->default_tweet; + $form->twitter_message->default_tweet->value = twitter::reset_default_tweet(); } else { $default_tweet = $form->twitter_message->default_tweet->value; } diff --git a/3.1/modules/twitter/helpers/twitter.php b/3.1/modules/twitter/helpers/twitter.php index 54df4e67..ceea12f5 100644 --- a/3.1/modules/twitter/helpers/twitter.php +++ b/3.1/modules/twitter/helpers/twitter.php @@ -133,4 +133,8 @@ class twitter_Core { } } + static function reset_default_tweet() { + return t("Check out this %type, '%title': %description %url"); + } + } From 6e605a809535050de2554dd2f037820c902d63a1 Mon Sep 17 00:00:00 2001 From: Chad Kieffer Date: Mon, 14 Feb 2011 22:52:22 -0700 Subject: [PATCH 298/300] Redirect after updating settings. --- 3.1/modules/twitter/controllers/admin_twitter.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/3.1/modules/twitter/controllers/admin_twitter.php b/3.1/modules/twitter/controllers/admin_twitter.php index 5c954953..7d1400ab 100644 --- a/3.1/modules/twitter/controllers/admin_twitter.php +++ b/3.1/modules/twitter/controllers/admin_twitter.php @@ -32,8 +32,7 @@ class Admin_Twitter_Controller extends Admin_Controller { $consumer_secret = $form->twitter_oauth->consumer_secret->value; $reset_tweet = $form->twitter_message->reset_tweet->value; if ($reset_tweet) { - $default_tweet = $this->default_tweet; - $form->twitter_message->default_tweet->value = twitter::reset_default_tweet(); + $default_tweet = twitter::reset_default_tweet(); } else { $default_tweet = $form->twitter_message->default_tweet->value; } @@ -43,7 +42,9 @@ class Admin_Twitter_Controller extends Admin_Controller { module::set_var("twitter", "consumer_secret", $consumer_secret); module::set_var("twitter", "default_tweet", $default_tweet); module::set_var("twitter", "shorten_urls", $shorten_urls); + message::success("Twitter settings saved"); + url::redirect("admin/twitter"); } } $is_registered = twitter::is_registered(); From 4b090fbe27550b5f33a1a48fc125bf954578649c Mon Sep 17 00:00:00 2001 From: dmolavi Date: Tue, 15 Feb 2011 20:29:36 -0500 Subject: [PATCH 299/300] Moving photoannotation to photo_annotation for consistancy w/ codex. --- .../controllers/admin_photoannotation.php | 0 .../controllers/photoannotation.php | 0 .../css/colorpicker.css | 322 +++++++++--------- .../css/photoannotation.css | 0 .../helpers/photoannotation.php | 0 .../helpers/photoannotation_block.php | 0 .../helpers/photoannotation_event.php | 0 .../helpers/photoannotation_installer.php | 0 .../helpers/photoannotation_theme.php | 0 .../images/blank.gif | Bin .../images/colorpicker_background.png | Bin .../images/colorpicker_hex.png | Bin .../images/colorpicker_hsb_b.png | Bin .../images/colorpicker_hsb_h.png | Bin .../images/colorpicker_hsb_s.png | Bin .../images/colorpicker_indic.gif | Bin .../images/colorpicker_overlay.png | Bin .../images/colorpicker_rgb_b.png | Bin .../images/colorpicker_rgb_g.png | Bin .../images/colorpicker_rgb_r.png | Bin .../images/colorpicker_select.gif | Bin .../images/colorpicker_submit.png | Bin .../images/custom_background.png | Bin .../images/custom_hex.png | Bin .../images/custom_hsb_b.png | Bin .../images/custom_hsb_h.png | Bin .../images/custom_hsb_s.png | Bin .../images/custom_indic.gif | Bin .../images/custom_rgb_b.png | Bin .../images/custom_rgb_g.png | Bin .../images/custom_rgb_r.png | Bin .../images/custom_submit.png | Bin .../images/delete.png | Bin .../images/edit.png | Bin .../images/select.png | Bin .../images/select2.png | Bin .../images/slider.png | Bin .../js/jquery.annotate.js | 0 .../js/jquery.annotate.min.js | 0 .../js/jquery.colorpicker.js | 0 .../js/jquery.colorpicker.min.js | 0 .../models/items_face.php | 0 .../models/items_note.php | 0 .../models/items_user.php | 0 .../models/photoannotation_notification.php | 0 .../module.info | 6 +- .../views/admin_photoannotation.html.php | 0 .../admin_photoannotation_converter.html.php | 0 ...n_photoannotation_tagsmaintanance.html.php | 0 .../views/photoannotation_block.html.php | 0 .../views/photoannotation_cloud.html.php | 0 .../photoannotation_highlight_block.html.php | 0 .../photoannotation_user_search.html.php | 0 53 files changed, 164 insertions(+), 164 deletions(-) rename 3.0/modules/{photoannotation => photo_annotation}/controllers/admin_photoannotation.php (100%) mode change 100755 => 100644 rename 3.0/modules/{photoannotation => photo_annotation}/controllers/photoannotation.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/css/colorpicker.css (94%) rename 3.0/modules/{photoannotation => photo_annotation}/css/photoannotation.css (100%) rename 3.0/modules/{photoannotation => photo_annotation}/helpers/photoannotation.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/helpers/photoannotation_block.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/helpers/photoannotation_event.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/helpers/photoannotation_installer.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/helpers/photoannotation_theme.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/blank.gif (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_background.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_hex.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_hsb_b.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_hsb_h.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_hsb_s.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_indic.gif (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_overlay.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_rgb_b.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_rgb_g.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_rgb_r.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_select.gif (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/colorpicker_submit.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_background.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_hex.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_hsb_b.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_hsb_h.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_hsb_s.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_indic.gif (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_rgb_b.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_rgb_g.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_rgb_r.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/custom_submit.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/delete.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/edit.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/select.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/select2.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/images/slider.png (100%) rename 3.0/modules/{photoannotation => photo_annotation}/js/jquery.annotate.js (100%) rename 3.0/modules/{photoannotation => photo_annotation}/js/jquery.annotate.min.js (100%) rename 3.0/modules/{photoannotation => photo_annotation}/js/jquery.colorpicker.js (100%) rename 3.0/modules/{photoannotation => photo_annotation}/js/jquery.colorpicker.min.js (100%) rename 3.0/modules/{photoannotation => photo_annotation}/models/items_face.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/models/items_note.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/models/items_user.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/models/photoannotation_notification.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/module.info (99%) rename 3.0/modules/{photoannotation => photo_annotation}/views/admin_photoannotation.html.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/views/admin_photoannotation_converter.html.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/views/admin_photoannotation_tagsmaintanance.html.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/views/photoannotation_block.html.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/views/photoannotation_cloud.html.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/views/photoannotation_highlight_block.html.php (100%) rename 3.0/modules/{photoannotation => photo_annotation}/views/photoannotation_user_search.html.php (100%) diff --git a/3.0/modules/photoannotation/controllers/admin_photoannotation.php b/3.0/modules/photo_annotation/controllers/admin_photoannotation.php old mode 100755 new mode 100644 similarity index 100% rename from 3.0/modules/photoannotation/controllers/admin_photoannotation.php rename to 3.0/modules/photo_annotation/controllers/admin_photoannotation.php diff --git a/3.0/modules/photoannotation/controllers/photoannotation.php b/3.0/modules/photo_annotation/controllers/photoannotation.php similarity index 100% rename from 3.0/modules/photoannotation/controllers/photoannotation.php rename to 3.0/modules/photo_annotation/controllers/photoannotation.php diff --git a/3.0/modules/photoannotation/css/colorpicker.css b/3.0/modules/photo_annotation/css/colorpicker.css similarity index 94% rename from 3.0/modules/photoannotation/css/colorpicker.css rename to 3.0/modules/photo_annotation/css/colorpicker.css index 05b02b48..0b3d5d93 100644 --- a/3.0/modules/photoannotation/css/colorpicker.css +++ b/3.0/modules/photo_annotation/css/colorpicker.css @@ -1,161 +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; -} +.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/3.0/modules/photoannotation/css/photoannotation.css b/3.0/modules/photo_annotation/css/photoannotation.css similarity index 100% rename from 3.0/modules/photoannotation/css/photoannotation.css rename to 3.0/modules/photo_annotation/css/photoannotation.css diff --git a/3.0/modules/photoannotation/helpers/photoannotation.php b/3.0/modules/photo_annotation/helpers/photoannotation.php similarity index 100% rename from 3.0/modules/photoannotation/helpers/photoannotation.php rename to 3.0/modules/photo_annotation/helpers/photoannotation.php diff --git a/3.0/modules/photoannotation/helpers/photoannotation_block.php b/3.0/modules/photo_annotation/helpers/photoannotation_block.php similarity index 100% rename from 3.0/modules/photoannotation/helpers/photoannotation_block.php rename to 3.0/modules/photo_annotation/helpers/photoannotation_block.php diff --git a/3.0/modules/photoannotation/helpers/photoannotation_event.php b/3.0/modules/photo_annotation/helpers/photoannotation_event.php similarity index 100% rename from 3.0/modules/photoannotation/helpers/photoannotation_event.php rename to 3.0/modules/photo_annotation/helpers/photoannotation_event.php diff --git a/3.0/modules/photoannotation/helpers/photoannotation_installer.php b/3.0/modules/photo_annotation/helpers/photoannotation_installer.php similarity index 100% rename from 3.0/modules/photoannotation/helpers/photoannotation_installer.php rename to 3.0/modules/photo_annotation/helpers/photoannotation_installer.php diff --git a/3.0/modules/photoannotation/helpers/photoannotation_theme.php b/3.0/modules/photo_annotation/helpers/photoannotation_theme.php similarity index 100% rename from 3.0/modules/photoannotation/helpers/photoannotation_theme.php rename to 3.0/modules/photo_annotation/helpers/photoannotation_theme.php diff --git a/3.0/modules/photoannotation/images/blank.gif b/3.0/modules/photo_annotation/images/blank.gif similarity index 100% rename from 3.0/modules/photoannotation/images/blank.gif rename to 3.0/modules/photo_annotation/images/blank.gif diff --git a/3.0/modules/photoannotation/images/colorpicker_background.png b/3.0/modules/photo_annotation/images/colorpicker_background.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_background.png rename to 3.0/modules/photo_annotation/images/colorpicker_background.png diff --git a/3.0/modules/photoannotation/images/colorpicker_hex.png b/3.0/modules/photo_annotation/images/colorpicker_hex.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_hex.png rename to 3.0/modules/photo_annotation/images/colorpicker_hex.png diff --git a/3.0/modules/photoannotation/images/colorpicker_hsb_b.png b/3.0/modules/photo_annotation/images/colorpicker_hsb_b.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_hsb_b.png rename to 3.0/modules/photo_annotation/images/colorpicker_hsb_b.png diff --git a/3.0/modules/photoannotation/images/colorpicker_hsb_h.png b/3.0/modules/photo_annotation/images/colorpicker_hsb_h.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_hsb_h.png rename to 3.0/modules/photo_annotation/images/colorpicker_hsb_h.png diff --git a/3.0/modules/photoannotation/images/colorpicker_hsb_s.png b/3.0/modules/photo_annotation/images/colorpicker_hsb_s.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_hsb_s.png rename to 3.0/modules/photo_annotation/images/colorpicker_hsb_s.png diff --git a/3.0/modules/photoannotation/images/colorpicker_indic.gif b/3.0/modules/photo_annotation/images/colorpicker_indic.gif similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_indic.gif rename to 3.0/modules/photo_annotation/images/colorpicker_indic.gif diff --git a/3.0/modules/photoannotation/images/colorpicker_overlay.png b/3.0/modules/photo_annotation/images/colorpicker_overlay.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_overlay.png rename to 3.0/modules/photo_annotation/images/colorpicker_overlay.png diff --git a/3.0/modules/photoannotation/images/colorpicker_rgb_b.png b/3.0/modules/photo_annotation/images/colorpicker_rgb_b.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_rgb_b.png rename to 3.0/modules/photo_annotation/images/colorpicker_rgb_b.png diff --git a/3.0/modules/photoannotation/images/colorpicker_rgb_g.png b/3.0/modules/photo_annotation/images/colorpicker_rgb_g.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_rgb_g.png rename to 3.0/modules/photo_annotation/images/colorpicker_rgb_g.png diff --git a/3.0/modules/photoannotation/images/colorpicker_rgb_r.png b/3.0/modules/photo_annotation/images/colorpicker_rgb_r.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_rgb_r.png rename to 3.0/modules/photo_annotation/images/colorpicker_rgb_r.png diff --git a/3.0/modules/photoannotation/images/colorpicker_select.gif b/3.0/modules/photo_annotation/images/colorpicker_select.gif similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_select.gif rename to 3.0/modules/photo_annotation/images/colorpicker_select.gif diff --git a/3.0/modules/photoannotation/images/colorpicker_submit.png b/3.0/modules/photo_annotation/images/colorpicker_submit.png similarity index 100% rename from 3.0/modules/photoannotation/images/colorpicker_submit.png rename to 3.0/modules/photo_annotation/images/colorpicker_submit.png diff --git a/3.0/modules/photoannotation/images/custom_background.png b/3.0/modules/photo_annotation/images/custom_background.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_background.png rename to 3.0/modules/photo_annotation/images/custom_background.png diff --git a/3.0/modules/photoannotation/images/custom_hex.png b/3.0/modules/photo_annotation/images/custom_hex.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_hex.png rename to 3.0/modules/photo_annotation/images/custom_hex.png diff --git a/3.0/modules/photoannotation/images/custom_hsb_b.png b/3.0/modules/photo_annotation/images/custom_hsb_b.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_hsb_b.png rename to 3.0/modules/photo_annotation/images/custom_hsb_b.png diff --git a/3.0/modules/photoannotation/images/custom_hsb_h.png b/3.0/modules/photo_annotation/images/custom_hsb_h.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_hsb_h.png rename to 3.0/modules/photo_annotation/images/custom_hsb_h.png diff --git a/3.0/modules/photoannotation/images/custom_hsb_s.png b/3.0/modules/photo_annotation/images/custom_hsb_s.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_hsb_s.png rename to 3.0/modules/photo_annotation/images/custom_hsb_s.png diff --git a/3.0/modules/photoannotation/images/custom_indic.gif b/3.0/modules/photo_annotation/images/custom_indic.gif similarity index 100% rename from 3.0/modules/photoannotation/images/custom_indic.gif rename to 3.0/modules/photo_annotation/images/custom_indic.gif diff --git a/3.0/modules/photoannotation/images/custom_rgb_b.png b/3.0/modules/photo_annotation/images/custom_rgb_b.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_rgb_b.png rename to 3.0/modules/photo_annotation/images/custom_rgb_b.png diff --git a/3.0/modules/photoannotation/images/custom_rgb_g.png b/3.0/modules/photo_annotation/images/custom_rgb_g.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_rgb_g.png rename to 3.0/modules/photo_annotation/images/custom_rgb_g.png diff --git a/3.0/modules/photoannotation/images/custom_rgb_r.png b/3.0/modules/photo_annotation/images/custom_rgb_r.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_rgb_r.png rename to 3.0/modules/photo_annotation/images/custom_rgb_r.png diff --git a/3.0/modules/photoannotation/images/custom_submit.png b/3.0/modules/photo_annotation/images/custom_submit.png similarity index 100% rename from 3.0/modules/photoannotation/images/custom_submit.png rename to 3.0/modules/photo_annotation/images/custom_submit.png diff --git a/3.0/modules/photoannotation/images/delete.png b/3.0/modules/photo_annotation/images/delete.png similarity index 100% rename from 3.0/modules/photoannotation/images/delete.png rename to 3.0/modules/photo_annotation/images/delete.png diff --git a/3.0/modules/photoannotation/images/edit.png b/3.0/modules/photo_annotation/images/edit.png similarity index 100% rename from 3.0/modules/photoannotation/images/edit.png rename to 3.0/modules/photo_annotation/images/edit.png diff --git a/3.0/modules/photoannotation/images/select.png b/3.0/modules/photo_annotation/images/select.png similarity index 100% rename from 3.0/modules/photoannotation/images/select.png rename to 3.0/modules/photo_annotation/images/select.png diff --git a/3.0/modules/photoannotation/images/select2.png b/3.0/modules/photo_annotation/images/select2.png similarity index 100% rename from 3.0/modules/photoannotation/images/select2.png rename to 3.0/modules/photo_annotation/images/select2.png diff --git a/3.0/modules/photoannotation/images/slider.png b/3.0/modules/photo_annotation/images/slider.png similarity index 100% rename from 3.0/modules/photoannotation/images/slider.png rename to 3.0/modules/photo_annotation/images/slider.png diff --git a/3.0/modules/photoannotation/js/jquery.annotate.js b/3.0/modules/photo_annotation/js/jquery.annotate.js similarity index 100% rename from 3.0/modules/photoannotation/js/jquery.annotate.js rename to 3.0/modules/photo_annotation/js/jquery.annotate.js diff --git a/3.0/modules/photoannotation/js/jquery.annotate.min.js b/3.0/modules/photo_annotation/js/jquery.annotate.min.js similarity index 100% rename from 3.0/modules/photoannotation/js/jquery.annotate.min.js rename to 3.0/modules/photo_annotation/js/jquery.annotate.min.js diff --git a/3.0/modules/photoannotation/js/jquery.colorpicker.js b/3.0/modules/photo_annotation/js/jquery.colorpicker.js similarity index 100% rename from 3.0/modules/photoannotation/js/jquery.colorpicker.js rename to 3.0/modules/photo_annotation/js/jquery.colorpicker.js diff --git a/3.0/modules/photoannotation/js/jquery.colorpicker.min.js b/3.0/modules/photo_annotation/js/jquery.colorpicker.min.js similarity index 100% rename from 3.0/modules/photoannotation/js/jquery.colorpicker.min.js rename to 3.0/modules/photo_annotation/js/jquery.colorpicker.min.js diff --git a/3.0/modules/photoannotation/models/items_face.php b/3.0/modules/photo_annotation/models/items_face.php similarity index 100% rename from 3.0/modules/photoannotation/models/items_face.php rename to 3.0/modules/photo_annotation/models/items_face.php diff --git a/3.0/modules/photoannotation/models/items_note.php b/3.0/modules/photo_annotation/models/items_note.php similarity index 100% rename from 3.0/modules/photoannotation/models/items_note.php rename to 3.0/modules/photo_annotation/models/items_note.php diff --git a/3.0/modules/photoannotation/models/items_user.php b/3.0/modules/photo_annotation/models/items_user.php similarity index 100% rename from 3.0/modules/photoannotation/models/items_user.php rename to 3.0/modules/photo_annotation/models/items_user.php diff --git a/3.0/modules/photoannotation/models/photoannotation_notification.php b/3.0/modules/photo_annotation/models/photoannotation_notification.php similarity index 100% rename from 3.0/modules/photoannotation/models/photoannotation_notification.php rename to 3.0/modules/photo_annotation/models/photoannotation_notification.php diff --git a/3.0/modules/photoannotation/module.info b/3.0/modules/photo_annotation/module.info similarity index 99% rename from 3.0/modules/photoannotation/module.info rename to 3.0/modules/photo_annotation/module.info index 133c2456..b603a755 100644 --- a/3.0/modules/photoannotation/module.info +++ b/3.0/modules/photo_annotation/module.info @@ -1,3 +1,3 @@ -name = "Photo Annotation" -description = "Allows you to assign tags and notes to areas on your photos. This module is partially compatible with the 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 run both modules at the same time." -version = 4 +name = "Photo Annotation" +description = "Allows you to assign tags and notes to areas on your photos. This module is partially compatible with the 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 run both modules at the same time." +version = 4 diff --git a/3.0/modules/photoannotation/views/admin_photoannotation.html.php b/3.0/modules/photo_annotation/views/admin_photoannotation.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/admin_photoannotation.html.php rename to 3.0/modules/photo_annotation/views/admin_photoannotation.html.php diff --git a/3.0/modules/photoannotation/views/admin_photoannotation_converter.html.php b/3.0/modules/photo_annotation/views/admin_photoannotation_converter.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/admin_photoannotation_converter.html.php rename to 3.0/modules/photo_annotation/views/admin_photoannotation_converter.html.php diff --git a/3.0/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php b/3.0/modules/photo_annotation/views/admin_photoannotation_tagsmaintanance.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/admin_photoannotation_tagsmaintanance.html.php rename to 3.0/modules/photo_annotation/views/admin_photoannotation_tagsmaintanance.html.php diff --git a/3.0/modules/photoannotation/views/photoannotation_block.html.php b/3.0/modules/photo_annotation/views/photoannotation_block.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/photoannotation_block.html.php rename to 3.0/modules/photo_annotation/views/photoannotation_block.html.php diff --git a/3.0/modules/photoannotation/views/photoannotation_cloud.html.php b/3.0/modules/photo_annotation/views/photoannotation_cloud.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/photoannotation_cloud.html.php rename to 3.0/modules/photo_annotation/views/photoannotation_cloud.html.php diff --git a/3.0/modules/photoannotation/views/photoannotation_highlight_block.html.php b/3.0/modules/photo_annotation/views/photoannotation_highlight_block.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/photoannotation_highlight_block.html.php rename to 3.0/modules/photo_annotation/views/photoannotation_highlight_block.html.php diff --git a/3.0/modules/photoannotation/views/photoannotation_user_search.html.php b/3.0/modules/photo_annotation/views/photoannotation_user_search.html.php similarity index 100% rename from 3.0/modules/photoannotation/views/photoannotation_user_search.html.php rename to 3.0/modules/photo_annotation/views/photoannotation_user_search.html.php From 442716a75b7aa54e13a2b560270669d988de7cdb Mon Sep 17 00:00:00 2001 From: dmolavi Date: Tue, 15 Feb 2011 20:34:00 -0500 Subject: [PATCH 300/300] Naming inconsistency between git and codex (git used british english, codex used american english). --- 3.0/modules/{gwtorganise => gwtorganize}/.classpath | 0 .../.externalToolBuilders/New_Builder.launch | 0 3.0/modules/{gwtorganise => gwtorganize}/.project | 0 .../com.google.appengine.eclipse.core.prefs | 0 .../.settings/com.google.gdt.eclipse.core.prefs | 0 .../.settings/com.google.gwt.eclipse.core.prefs | 0 3.0/modules/{gwtorganise => gwtorganize}/build.xml | 0 .../controllers/admin_gwtorganise.php | 0 .../controllers/admin_upload_configure.php | 0 .../controllers/json_album.php | 0 .../helpers/gwtorganise_event.php | 0 .../helpers/gwtorganise_installer.php | 0 .../helpers/revision.php | 0 .../helpers/upload_configuration.php | 0 .../{gwtorganise => gwtorganize}/module.info | 0 .../src/META-INF/jdoconfig.xml | 0 .../src/com/gloopics/g3viewer/G3viewer.gwt.xml | 0 .../src/com/gloopics/g3viewer/client/Album.java | 0 .../g3viewer/client/AlbumItemDropContainer.java | 0 .../src/com/gloopics/g3viewer/client/AlbumTree.java | 0 .../g3viewer/client/AlbumTreeDropController.java | 0 .../com/gloopics/g3viewer/client/AsyncResizer.java | 0 .../com/gloopics/g3viewer/client/AsyncRunner.java | 0 .../gloopics/g3viewer/client/ConfirmDialogBox.java | 0 .../g3viewer/client/DropZoneController.java | 0 .../src/com/gloopics/g3viewer/client/G3Viewer.java | 0 .../com/gloopics/g3viewer/client/HttpDialogBox.java | 0 .../gloopics/g3viewer/client/HttpDialogHandler.java | 0 .../g3viewer/client/HttpSuccessHandler.java | 0 .../gloopics/g3viewer/client/ImageDialogBox.java | 0 .../gloopics/g3viewer/client/InformationBar.java | 0 .../src/com/gloopics/g3viewer/client/Item.java | 0 .../g3viewer/client/JSONResponseCallback.java | 0 .../g3viewer/client/JSONResponseTextHandler.java | 0 .../src/com/gloopics/g3viewer/client/Loading.java | 0 .../g3viewer/client/MyPickupDragController.java | 0 .../com/gloopics/g3viewer/client/ResizeOptions.java | 0 .../com/gloopics/g3viewer/client/UploadControl.java | 0 .../g3viewer/client/UploadControlNoGears.java | 0 .../com/gloopics/g3viewer/client/UploadFile.java | 0 .../src/com/gloopics/g3viewer/client/Utils.java | 0 .../src/com/gloopics/g3viewer/client/View.java | 0 .../com/gloopics/g3viewer/client/canvas/Canvas.java | 0 .../gloopics/g3viewer/client/canvas/Factory.java | 0 .../g3viewer/client/canvas/ResizeFilter.java | 0 .../g3viewer/client/dnddesktop/DesktopDrop.java | 0 .../g3viewer/client/dnddesktop/DesktopDropBase.java | 0 .../g3viewer/client/dnddesktop/DesktopDropFile.java | 0 .../client/dnddesktop/DesktopDropFileIE.java | 0 .../client/dnddesktop/DesktopDroppableWidget.java | 0 .../client/dnddesktop/DndDesktopFactory.java | 0 .../client/dnddesktop/DndDesktopFactoryIE.java | 0 .../client/dnddesktop/DndDesktopFactoryNoGears.java | 0 .../src/com/gloopics/g3viewer/public/G3viewer.css | 0 .../src/com/gloopics/g3viewer/public/loading.gif | Bin .../src/log4j.properties | 0 .../views/gwtorganise_view.html.php | 0 .../{gwtorganise => gwtorganize}/war/.htaccess | 0 .../war/WEB-INF/classes/META-INF/jdoconfig.xml | 0 .../classes/com/gloopics/g3viewer/G3viewer.gwt.xml | 0 .../com/gloopics/g3viewer/client/Album$1.class | Bin .../com/gloopics/g3viewer/client/Album$10.class | Bin .../com/gloopics/g3viewer/client/Album$11.class | Bin .../com/gloopics/g3viewer/client/Album$12.class | Bin .../com/gloopics/g3viewer/client/Album$2$1.class | Bin .../com/gloopics/g3viewer/client/Album$2.class | Bin .../com/gloopics/g3viewer/client/Album$3$1.class | Bin .../com/gloopics/g3viewer/client/Album$3.class | Bin .../com/gloopics/g3viewer/client/Album$4$1.class | Bin .../com/gloopics/g3viewer/client/Album$4.class | Bin .../com/gloopics/g3viewer/client/Album$5.class | Bin .../com/gloopics/g3viewer/client/Album$6.class | Bin .../com/gloopics/g3viewer/client/Album$7.class | Bin .../com/gloopics/g3viewer/client/Album$8.class | Bin .../com/gloopics/g3viewer/client/Album$9.class | Bin .../com/gloopics/g3viewer/client/Album.class | Bin .../g3viewer/client/AlbumItemDropContainer.class | Bin .../com/gloopics/g3viewer/client/AlbumTree$1.class | Bin .../com/gloopics/g3viewer/client/AlbumTree.class | Bin .../g3viewer/client/AlbumTreeDropController.class | Bin .../com/gloopics/g3viewer/client/AsyncResizer.class | Bin .../com/gloopics/g3viewer/client/AsyncRunner.class | Bin .../g3viewer/client/ConfirmDialogBox$1.class | Bin .../g3viewer/client/ConfirmDialogBox$2.class | Bin .../client/ConfirmDialogBox$ConfirmCallBack.class | Bin .../gloopics/g3viewer/client/ConfirmDialogBox.class | Bin .../g3viewer/client/DropZoneController.class | Bin .../com/gloopics/g3viewer/client/G3Viewer$1$1.class | Bin .../com/gloopics/g3viewer/client/G3Viewer$1.class | Bin .../com/gloopics/g3viewer/client/G3Viewer$2.class | Bin .../g3viewer/client/G3Viewer$ErrorDialog$1.class | Bin .../g3viewer/client/G3Viewer$ErrorDialog.class | Bin .../g3viewer/client/G3Viewer$SimplePanelEx.class | Bin .../com/gloopics/g3viewer/client/G3Viewer.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$1.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$2.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$3.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$4.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$5.class | Bin .../client/HttpDialogBox$RequestCallbackImpl.class | Bin .../gloopics/g3viewer/client/HttpDialogBox.class | Bin .../g3viewer/client/HttpDialogHandler.class | Bin .../g3viewer/client/HttpSuccessHandler.class | Bin .../gloopics/g3viewer/client/ImageDialogBox$1.class | Bin .../gloopics/g3viewer/client/ImageDialogBox$2.class | Bin .../gloopics/g3viewer/client/ImageDialogBox$3.class | Bin .../gloopics/g3viewer/client/ImageDialogBox.class | Bin .../g3viewer/client/InformationBar$1$1.class | Bin .../gloopics/g3viewer/client/InformationBar$1.class | Bin .../gloopics/g3viewer/client/InformationBar.class | Bin .../com/gloopics/g3viewer/client/Item$1.class | Bin .../com/gloopics/g3viewer/client/Item$2.class | Bin .../com/gloopics/g3viewer/client/Item$3.class | Bin .../com/gloopics/g3viewer/client/Item$4$1.class | Bin .../com/gloopics/g3viewer/client/Item$4.class | Bin .../com/gloopics/g3viewer/client/Item$5$1.class | Bin .../com/gloopics/g3viewer/client/Item$5.class | Bin .../com/gloopics/g3viewer/client/Item$6$1.class | Bin .../com/gloopics/g3viewer/client/Item$6.class | Bin .../com/gloopics/g3viewer/client/Item$7$1.class | Bin .../com/gloopics/g3viewer/client/Item$7.class | Bin .../com/gloopics/g3viewer/client/Item$8$1.class | Bin .../com/gloopics/g3viewer/client/Item$8.class | Bin .../com/gloopics/g3viewer/client/Item$9.class | Bin .../classes/com/gloopics/g3viewer/client/Item.class | Bin .../g3viewer/client/JSONResponseCallback.class | Bin .../g3viewer/client/JSONResponseTextHandler.class | Bin .../com/gloopics/g3viewer/client/Loading.class | Bin .../g3viewer/client/MyPickupDragController.class | Bin .../gloopics/g3viewer/client/ResizeOptions.class | Bin .../gloopics/g3viewer/client/UploadControl.class | Bin .../g3viewer/client/UploadControlNoGears.class | Bin .../com/gloopics/g3viewer/client/UploadFile$1.class | Bin .../com/gloopics/g3viewer/client/UploadFile$2.class | Bin .../com/gloopics/g3viewer/client/UploadFile$3.class | Bin .../g3viewer/client/UploadFile$ProgressBar.class | Bin .../com/gloopics/g3viewer/client/UploadFile.class | Bin .../com/gloopics/g3viewer/client/Utils.class | Bin .../com/gloopics/g3viewer/client/View$1$1$1.class | Bin .../com/gloopics/g3viewer/client/View$1$1.class | Bin .../com/gloopics/g3viewer/client/View$1.class | Bin .../com/gloopics/g3viewer/client/View$2$1.class | Bin .../com/gloopics/g3viewer/client/View$2.class | Bin .../com/gloopics/g3viewer/client/View$3$1.class | Bin .../com/gloopics/g3viewer/client/View$3.class | Bin .../classes/com/gloopics/g3viewer/client/View.class | Bin .../gloopics/g3viewer/client/canvas/Canvas.class | Bin .../gloopics/g3viewer/client/canvas/Factory.class | Bin .../g3viewer/client/canvas/ResizeFilter$1.class | Bin .../g3viewer/client/canvas/ResizeFilter$2.class | Bin .../g3viewer/client/canvas/ResizeFilter.class | Bin .../g3viewer/client/dnddesktop/DesktopDrop.class | Bin .../client/dnddesktop/DesktopDropBase.class | Bin .../client/dnddesktop/DesktopDropFile.class | Bin .../client/dnddesktop/DesktopDropFileIE.class | Bin .../client/dnddesktop/DesktopDroppableWidget.class | Bin .../client/dnddesktop/DndDesktopFactory.class | Bin .../client/dnddesktop/DndDesktopFactoryIE.class | Bin .../dnddesktop/DndDesktopFactoryNoGears$1.class | Bin .../dnddesktop/DndDesktopFactoryNoGears.class | Bin .../com/gloopics/g3viewer/public/G3viewer.css | 0 .../com/gloopics/g3viewer/public/loading.gif | Bin .../war/WEB-INF/classes/log4j.properties | 0 .../war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar | Bin .../war/WEB-INF/lib/appengine-api-labs-1.2.5.jar | Bin .../war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar | Bin .../war/WEB-INF/lib/datanucleus-core-1.1.5.jar | Bin .../war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar | Bin .../war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar | Bin .../war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar | Bin .../war/WEB-INF/lib/gwt-servlet.jar | Bin .../war/WEB-INF/lib/jdo2-api-2.3-eb.jar | Bin .../0A9476898799A150D840F0B1C3672921.cache.png | Bin .../0D97DF37194D1924CC80394AAA96B9A3.cache.html | 0 .../27AC86F0820D8F960DBF73C151C0332B.cache.html | 0 .../396F806CD63ABD414BFBB9D57429F05B.cache.png | Bin .../4AFE598FDFDF189DD61F57E554328B10.cache.html | 0 .../4E8EC2279CB4B46228EFF0682ED166A4.cache.html | 0 .../4F7AD7D8299143D876CB4071BE00BF02.cache.html | 0 .../6462B363383D23B8418857B7A6FAD85B.cache.html | 0 .../71ED95F3DFB964762667E45E2922704D.cache.html | 0 .../8603379B5088782D2C0620FAE856E112.cache.png | Bin .../884CB866FECF37EDDE4914CA60AF2511.cache.html | 0 .../9DC95FB4BEC084EF810751F04B440FD7.cache.html | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html | 0 .../CE15F73DB4EDED1CF8F93F95A728792D.cache.html | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html | 0 .../DF7764EEC1903CD03C9545B354D8D8E4.cache.png | Bin .../E44767377485D18D6B6864F65BA8EF73.cache.png | Bin .../EDC7827FEEA59EE44AD790B1C6430C45.cache.png | Bin .../war/g3viewer/G3viewer.css | 0 .../war/g3viewer/clear.cache.gif | Bin .../0D97DF37194D1924CC80394AAA96B9A3/1.cache.js | 0 .../0D97DF37194D1924CC80394AAA96B9A3/2.cache.js | 0 .../0D97DF37194D1924CC80394AAA96B9A3/3.cache.js | 0 .../27AC86F0820D8F960DBF73C151C0332B/1.cache.js | 0 .../27AC86F0820D8F960DBF73C151C0332B/2.cache.js | 0 .../27AC86F0820D8F960DBF73C151C0332B/3.cache.js | 0 .../4AFE598FDFDF189DD61F57E554328B10/1.cache.js | 0 .../4AFE598FDFDF189DD61F57E554328B10/2.cache.js | 0 .../4AFE598FDFDF189DD61F57E554328B10/3.cache.js | 0 .../4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js | 0 .../4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js | 0 .../4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js | 0 .../4F7AD7D8299143D876CB4071BE00BF02/1.cache.js | 0 .../4F7AD7D8299143D876CB4071BE00BF02/2.cache.js | 0 .../4F7AD7D8299143D876CB4071BE00BF02/3.cache.js | 0 .../6462B363383D23B8418857B7A6FAD85B/1.cache.js | 0 .../6462B363383D23B8418857B7A6FAD85B/2.cache.js | 0 .../6462B363383D23B8418857B7A6FAD85B/3.cache.js | 0 .../71ED95F3DFB964762667E45E2922704D/1.cache.js | 0 .../71ED95F3DFB964762667E45E2922704D/2.cache.js | 0 .../71ED95F3DFB964762667E45E2922704D/3.cache.js | 0 .../884CB866FECF37EDDE4914CA60AF2511/1.cache.js | 0 .../884CB866FECF37EDDE4914CA60AF2511/2.cache.js | 0 .../884CB866FECF37EDDE4914CA60AF2511/3.cache.js | 0 .../9DC95FB4BEC084EF810751F04B440FD7/1.cache.js | 0 .../9DC95FB4BEC084EF810751F04B440FD7/2.cache.js | 0 .../9DC95FB4BEC084EF810751F04B440FD7/3.cache.js | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js | 0 .../CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js | 0 .../CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js | 0 .../CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js | 0 .../war/g3viewer/g3viewer.nocache.js | 0 .../war/g3viewer/gears_init.js | 0 .../war/g3viewer/gwt/standard/images/corner.png | Bin .../war/g3viewer/gwt/standard/images/corner_ie6.png | Bin .../war/g3viewer/gwt/standard/images/hborder.png | Bin .../g3viewer/gwt/standard/images/hborder_ie6.png | Bin .../standard/images/ie6/corner_dialog_topleft.png | Bin .../standard/images/ie6/corner_dialog_topright.png | Bin .../gwt/standard/images/ie6/hborder_blue_shadow.png | Bin .../gwt/standard/images/ie6/hborder_gray_shadow.png | Bin .../gwt/standard/images/ie6/vborder_blue_shadow.png | Bin .../gwt/standard/images/ie6/vborder_gray_shadow.png | Bin .../war/g3viewer/gwt/standard/images/vborder.png | Bin .../g3viewer/gwt/standard/images/vborder_ie6.png | Bin .../war/g3viewer/gwt/standard/standard.css | 0 .../war/g3viewer/gwt/standard/standard_rtl.css | 0 .../war/g3viewer/hosted.html | 0 .../war/g3viewer/loading.gif | Bin .../{gwtorganise => gwtorganize}/war/index.php | 0 3.1/modules/{gwtorganise => gwtorganize}/.classpath | 0 .../.externalToolBuilders/New_Builder.launch | 0 3.1/modules/{gwtorganise => gwtorganize}/.project | 0 .../com.google.appengine.eclipse.core.prefs | 0 .../.settings/com.google.gdt.eclipse.core.prefs | 0 .../.settings/com.google.gwt.eclipse.core.prefs | 0 3.1/modules/{gwtorganise => gwtorganize}/build.xml | 0 .../controllers/admin_gwtorganise.php | 0 .../controllers/admin_upload_configure.php | 0 .../controllers/json_album.php | 0 .../helpers/gwtorganise_event.php | 0 .../helpers/gwtorganise_installer.php | 0 .../helpers/revision.php | 0 .../helpers/upload_configuration.php | 0 .../{gwtorganise => gwtorganize}/module.info | 0 .../src/META-INF/jdoconfig.xml | 0 .../src/com/gloopics/g3viewer/G3viewer.gwt.xml | 0 .../src/com/gloopics/g3viewer/client/Album.java | 0 .../g3viewer/client/AlbumItemDropContainer.java | 0 .../src/com/gloopics/g3viewer/client/AlbumTree.java | 0 .../g3viewer/client/AlbumTreeDropController.java | 0 .../com/gloopics/g3viewer/client/AsyncResizer.java | 0 .../com/gloopics/g3viewer/client/AsyncRunner.java | 0 .../gloopics/g3viewer/client/ConfirmDialogBox.java | 0 .../g3viewer/client/DropZoneController.java | 0 .../src/com/gloopics/g3viewer/client/G3Viewer.java | 0 .../com/gloopics/g3viewer/client/HttpDialogBox.java | 0 .../gloopics/g3viewer/client/HttpDialogHandler.java | 0 .../g3viewer/client/HttpSuccessHandler.java | 0 .../gloopics/g3viewer/client/ImageDialogBox.java | 0 .../gloopics/g3viewer/client/InformationBar.java | 0 .../src/com/gloopics/g3viewer/client/Item.java | 0 .../g3viewer/client/JSONResponseCallback.java | 0 .../g3viewer/client/JSONResponseTextHandler.java | 0 .../src/com/gloopics/g3viewer/client/Loading.java | 0 .../g3viewer/client/MyPickupDragController.java | 0 .../com/gloopics/g3viewer/client/ResizeOptions.java | 0 .../com/gloopics/g3viewer/client/UploadControl.java | 0 .../g3viewer/client/UploadControlNoGears.java | 0 .../com/gloopics/g3viewer/client/UploadFile.java | 0 .../src/com/gloopics/g3viewer/client/Utils.java | 0 .../src/com/gloopics/g3viewer/client/View.java | 0 .../com/gloopics/g3viewer/client/canvas/Canvas.java | 0 .../gloopics/g3viewer/client/canvas/Factory.java | 0 .../g3viewer/client/canvas/ResizeFilter.java | 0 .../g3viewer/client/dnddesktop/DesktopDrop.java | 0 .../g3viewer/client/dnddesktop/DesktopDropBase.java | 0 .../g3viewer/client/dnddesktop/DesktopDropFile.java | 0 .../client/dnddesktop/DesktopDropFileIE.java | 0 .../client/dnddesktop/DesktopDroppableWidget.java | 0 .../client/dnddesktop/DndDesktopFactory.java | 0 .../client/dnddesktop/DndDesktopFactoryIE.java | 0 .../client/dnddesktop/DndDesktopFactoryNoGears.java | 0 .../src/com/gloopics/g3viewer/public/G3viewer.css | 0 .../src/com/gloopics/g3viewer/public/loading.gif | Bin .../src/log4j.properties | 0 .../views/gwtorganise_view.html.php | 0 .../{gwtorganise => gwtorganize}/war/.htaccess | 0 .../war/WEB-INF/classes/META-INF/jdoconfig.xml | 0 .../classes/com/gloopics/g3viewer/G3viewer.gwt.xml | 0 .../com/gloopics/g3viewer/client/Album$1.class | Bin .../com/gloopics/g3viewer/client/Album$10.class | Bin .../com/gloopics/g3viewer/client/Album$11.class | Bin .../com/gloopics/g3viewer/client/Album$12.class | Bin .../com/gloopics/g3viewer/client/Album$2$1.class | Bin .../com/gloopics/g3viewer/client/Album$2.class | Bin .../com/gloopics/g3viewer/client/Album$3$1.class | Bin .../com/gloopics/g3viewer/client/Album$3.class | Bin .../com/gloopics/g3viewer/client/Album$4$1.class | Bin .../com/gloopics/g3viewer/client/Album$4.class | Bin .../com/gloopics/g3viewer/client/Album$5.class | Bin .../com/gloopics/g3viewer/client/Album$6.class | Bin .../com/gloopics/g3viewer/client/Album$7.class | Bin .../com/gloopics/g3viewer/client/Album$8.class | Bin .../com/gloopics/g3viewer/client/Album$9.class | Bin .../com/gloopics/g3viewer/client/Album.class | Bin .../g3viewer/client/AlbumItemDropContainer.class | Bin .../com/gloopics/g3viewer/client/AlbumTree$1.class | Bin .../com/gloopics/g3viewer/client/AlbumTree.class | Bin .../g3viewer/client/AlbumTreeDropController.class | Bin .../com/gloopics/g3viewer/client/AsyncResizer.class | Bin .../com/gloopics/g3viewer/client/AsyncRunner.class | Bin .../g3viewer/client/ConfirmDialogBox$1.class | Bin .../g3viewer/client/ConfirmDialogBox$2.class | Bin .../client/ConfirmDialogBox$ConfirmCallBack.class | Bin .../gloopics/g3viewer/client/ConfirmDialogBox.class | Bin .../g3viewer/client/DropZoneController.class | Bin .../com/gloopics/g3viewer/client/G3Viewer$1$1.class | Bin .../com/gloopics/g3viewer/client/G3Viewer$1.class | Bin .../com/gloopics/g3viewer/client/G3Viewer$2.class | Bin .../g3viewer/client/G3Viewer$ErrorDialog$1.class | Bin .../g3viewer/client/G3Viewer$ErrorDialog.class | Bin .../g3viewer/client/G3Viewer$SimplePanelEx.class | Bin .../com/gloopics/g3viewer/client/G3Viewer.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$1.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$2.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$3.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$4.class | Bin .../gloopics/g3viewer/client/HttpDialogBox$5.class | Bin .../client/HttpDialogBox$RequestCallbackImpl.class | Bin .../gloopics/g3viewer/client/HttpDialogBox.class | Bin .../g3viewer/client/HttpDialogHandler.class | Bin .../g3viewer/client/HttpSuccessHandler.class | Bin .../gloopics/g3viewer/client/ImageDialogBox$1.class | Bin .../gloopics/g3viewer/client/ImageDialogBox$2.class | Bin .../gloopics/g3viewer/client/ImageDialogBox$3.class | Bin .../gloopics/g3viewer/client/ImageDialogBox.class | Bin .../g3viewer/client/InformationBar$1$1.class | Bin .../gloopics/g3viewer/client/InformationBar$1.class | Bin .../gloopics/g3viewer/client/InformationBar.class | Bin .../com/gloopics/g3viewer/client/Item$1.class | Bin .../com/gloopics/g3viewer/client/Item$2.class | Bin .../com/gloopics/g3viewer/client/Item$3.class | Bin .../com/gloopics/g3viewer/client/Item$4$1.class | Bin .../com/gloopics/g3viewer/client/Item$4.class | Bin .../com/gloopics/g3viewer/client/Item$5$1.class | Bin .../com/gloopics/g3viewer/client/Item$5.class | Bin .../com/gloopics/g3viewer/client/Item$6$1.class | Bin .../com/gloopics/g3viewer/client/Item$6.class | Bin .../com/gloopics/g3viewer/client/Item$7$1.class | Bin .../com/gloopics/g3viewer/client/Item$7.class | Bin .../com/gloopics/g3viewer/client/Item$8$1.class | Bin .../com/gloopics/g3viewer/client/Item$8.class | Bin .../com/gloopics/g3viewer/client/Item$9.class | Bin .../classes/com/gloopics/g3viewer/client/Item.class | Bin .../g3viewer/client/JSONResponseCallback.class | Bin .../g3viewer/client/JSONResponseTextHandler.class | Bin .../com/gloopics/g3viewer/client/Loading.class | Bin .../g3viewer/client/MyPickupDragController.class | Bin .../gloopics/g3viewer/client/ResizeOptions.class | Bin .../gloopics/g3viewer/client/UploadControl.class | Bin .../g3viewer/client/UploadControlNoGears.class | Bin .../com/gloopics/g3viewer/client/UploadFile$1.class | Bin .../com/gloopics/g3viewer/client/UploadFile$2.class | Bin .../com/gloopics/g3viewer/client/UploadFile$3.class | Bin .../g3viewer/client/UploadFile$ProgressBar.class | Bin .../com/gloopics/g3viewer/client/UploadFile.class | Bin .../com/gloopics/g3viewer/client/Utils.class | Bin .../com/gloopics/g3viewer/client/View$1$1$1.class | Bin .../com/gloopics/g3viewer/client/View$1$1.class | Bin .../com/gloopics/g3viewer/client/View$1.class | Bin .../com/gloopics/g3viewer/client/View$2$1.class | Bin .../com/gloopics/g3viewer/client/View$2.class | Bin .../com/gloopics/g3viewer/client/View$3$1.class | Bin .../com/gloopics/g3viewer/client/View$3.class | Bin .../classes/com/gloopics/g3viewer/client/View.class | Bin .../gloopics/g3viewer/client/canvas/Canvas.class | Bin .../gloopics/g3viewer/client/canvas/Factory.class | Bin .../g3viewer/client/canvas/ResizeFilter$1.class | Bin .../g3viewer/client/canvas/ResizeFilter$2.class | Bin .../g3viewer/client/canvas/ResizeFilter.class | Bin .../g3viewer/client/dnddesktop/DesktopDrop.class | Bin .../client/dnddesktop/DesktopDropBase.class | Bin .../client/dnddesktop/DesktopDropFile.class | Bin .../client/dnddesktop/DesktopDropFileIE.class | Bin .../client/dnddesktop/DesktopDroppableWidget.class | Bin .../client/dnddesktop/DndDesktopFactory.class | Bin .../client/dnddesktop/DndDesktopFactoryIE.class | Bin .../dnddesktop/DndDesktopFactoryNoGears$1.class | Bin .../dnddesktop/DndDesktopFactoryNoGears.class | Bin .../com/gloopics/g3viewer/public/G3viewer.css | 0 .../com/gloopics/g3viewer/public/loading.gif | Bin .../war/WEB-INF/classes/log4j.properties | 0 .../war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar | Bin .../war/WEB-INF/lib/appengine-api-labs-1.2.5.jar | Bin .../war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar | Bin .../war/WEB-INF/lib/datanucleus-core-1.1.5.jar | Bin .../war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar | Bin .../war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar | Bin .../war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar | Bin .../war/WEB-INF/lib/gwt-servlet.jar | Bin .../war/WEB-INF/lib/jdo2-api-2.3-eb.jar | Bin .../0A9476898799A150D840F0B1C3672921.cache.png | Bin .../0D97DF37194D1924CC80394AAA96B9A3.cache.html | 0 .../27AC86F0820D8F960DBF73C151C0332B.cache.html | 0 .../396F806CD63ABD414BFBB9D57429F05B.cache.png | Bin .../4AFE598FDFDF189DD61F57E554328B10.cache.html | 0 .../4E8EC2279CB4B46228EFF0682ED166A4.cache.html | 0 .../4F7AD7D8299143D876CB4071BE00BF02.cache.html | 0 .../6462B363383D23B8418857B7A6FAD85B.cache.html | 0 .../71ED95F3DFB964762667E45E2922704D.cache.html | 0 .../8603379B5088782D2C0620FAE856E112.cache.png | Bin .../884CB866FECF37EDDE4914CA60AF2511.cache.html | 0 .../9DC95FB4BEC084EF810751F04B440FD7.cache.html | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html | 0 .../CE15F73DB4EDED1CF8F93F95A728792D.cache.html | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html | 0 .../DF7764EEC1903CD03C9545B354D8D8E4.cache.png | Bin .../E44767377485D18D6B6864F65BA8EF73.cache.png | Bin .../EDC7827FEEA59EE44AD790B1C6430C45.cache.png | Bin .../war/g3viewer/G3viewer.css | 0 .../war/g3viewer/clear.cache.gif | Bin .../0D97DF37194D1924CC80394AAA96B9A3/1.cache.js | 0 .../0D97DF37194D1924CC80394AAA96B9A3/2.cache.js | 0 .../0D97DF37194D1924CC80394AAA96B9A3/3.cache.js | 0 .../27AC86F0820D8F960DBF73C151C0332B/1.cache.js | 0 .../27AC86F0820D8F960DBF73C151C0332B/2.cache.js | 0 .../27AC86F0820D8F960DBF73C151C0332B/3.cache.js | 0 .../4AFE598FDFDF189DD61F57E554328B10/1.cache.js | 0 .../4AFE598FDFDF189DD61F57E554328B10/2.cache.js | 0 .../4AFE598FDFDF189DD61F57E554328B10/3.cache.js | 0 .../4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js | 0 .../4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js | 0 .../4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js | 0 .../4F7AD7D8299143D876CB4071BE00BF02/1.cache.js | 0 .../4F7AD7D8299143D876CB4071BE00BF02/2.cache.js | 0 .../4F7AD7D8299143D876CB4071BE00BF02/3.cache.js | 0 .../6462B363383D23B8418857B7A6FAD85B/1.cache.js | 0 .../6462B363383D23B8418857B7A6FAD85B/2.cache.js | 0 .../6462B363383D23B8418857B7A6FAD85B/3.cache.js | 0 .../71ED95F3DFB964762667E45E2922704D/1.cache.js | 0 .../71ED95F3DFB964762667E45E2922704D/2.cache.js | 0 .../71ED95F3DFB964762667E45E2922704D/3.cache.js | 0 .../884CB866FECF37EDDE4914CA60AF2511/1.cache.js | 0 .../884CB866FECF37EDDE4914CA60AF2511/2.cache.js | 0 .../884CB866FECF37EDDE4914CA60AF2511/3.cache.js | 0 .../9DC95FB4BEC084EF810751F04B440FD7/1.cache.js | 0 .../9DC95FB4BEC084EF810751F04B440FD7/2.cache.js | 0 .../9DC95FB4BEC084EF810751F04B440FD7/3.cache.js | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js | 0 .../A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js | 0 .../CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js | 0 .../CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js | 0 .../CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js | 0 .../D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js | 0 .../war/g3viewer/g3viewer.nocache.js | 0 .../war/g3viewer/gears_init.js | 0 .../war/g3viewer/gwt/standard/images/corner.png | Bin .../war/g3viewer/gwt/standard/images/corner_ie6.png | Bin .../war/g3viewer/gwt/standard/images/hborder.png | Bin .../g3viewer/gwt/standard/images/hborder_ie6.png | Bin .../standard/images/ie6/corner_dialog_topleft.png | Bin .../standard/images/ie6/corner_dialog_topright.png | Bin .../gwt/standard/images/ie6/hborder_blue_shadow.png | Bin .../gwt/standard/images/ie6/hborder_gray_shadow.png | Bin .../gwt/standard/images/ie6/vborder_blue_shadow.png | Bin .../gwt/standard/images/ie6/vborder_gray_shadow.png | Bin .../war/g3viewer/gwt/standard/images/vborder.png | Bin .../g3viewer/gwt/standard/images/vborder_ie6.png | Bin .../war/g3viewer/gwt/standard/standard.css | 0 .../war/g3viewer/gwt/standard/standard_rtl.css | 0 .../war/g3viewer/hosted.html | 0 .../war/g3viewer/loading.gif | Bin .../{gwtorganise => gwtorganize}/war/index.php | 0 494 files changed, 0 insertions(+), 0 deletions(-) rename 3.0/modules/{gwtorganise => gwtorganize}/.classpath (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/.externalToolBuilders/New_Builder.launch (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/.project (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/.settings/com.google.appengine.eclipse.core.prefs (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/.settings/com.google.gdt.eclipse.core.prefs (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/.settings/com.google.gwt.eclipse.core.prefs (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/build.xml (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/controllers/admin_gwtorganise.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/controllers/admin_upload_configure.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/controllers/json_album.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/helpers/gwtorganise_event.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/helpers/gwtorganise_installer.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/helpers/revision.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/helpers/upload_configuration.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/module.info (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/META-INF/jdoconfig.xml (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/G3viewer.gwt.xml (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Album.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AlbumTree.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AsyncResizer.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AsyncRunner.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/DropZoneController.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/G3Viewer.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/HttpDialogBox.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/HttpDialogHandler.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/ImageDialogBox.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/InformationBar.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Item.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/JSONResponseCallback.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Loading.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/MyPickupDragController.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/ResizeOptions.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/UploadControl.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/UploadControlNoGears.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/UploadFile.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Utils.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/View.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/canvas/Canvas.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/canvas/Factory.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/public/G3viewer.css (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/public/loading.gif (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/src/log4j.properties (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/views/gwtorganise_view.html.php (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/.htaccess (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/META-INF/jdoconfig.xml (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/log4j.properties (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/datanucleus-core-1.1.5.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/gwt-servlet.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/jdo2-api-2.3-eb.jar (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/G3viewer.css (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/clear.cache.gif (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/g3viewer.nocache.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gears_init.js (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/corner.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/corner_ie6.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/hborder.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/hborder_ie6.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/vborder.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/vborder_ie6.png (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/standard.css (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/standard_rtl.css (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/hosted.html (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/g3viewer/loading.gif (100%) rename 3.0/modules/{gwtorganise => gwtorganize}/war/index.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/.classpath (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/.externalToolBuilders/New_Builder.launch (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/.project (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/.settings/com.google.appengine.eclipse.core.prefs (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/.settings/com.google.gdt.eclipse.core.prefs (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/.settings/com.google.gwt.eclipse.core.prefs (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/build.xml (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/controllers/admin_gwtorganise.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/controllers/admin_upload_configure.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/controllers/json_album.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/helpers/gwtorganise_event.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/helpers/gwtorganise_installer.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/helpers/revision.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/helpers/upload_configuration.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/module.info (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/META-INF/jdoconfig.xml (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/G3viewer.gwt.xml (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Album.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AlbumTree.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AsyncResizer.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/AsyncRunner.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/DropZoneController.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/G3Viewer.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/HttpDialogBox.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/HttpDialogHandler.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/ImageDialogBox.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/InformationBar.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Item.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/JSONResponseCallback.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Loading.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/MyPickupDragController.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/ResizeOptions.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/UploadControl.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/UploadControlNoGears.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/UploadFile.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/Utils.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/View.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/canvas/Canvas.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/canvas/Factory.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/public/G3viewer.css (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/com/gloopics/g3viewer/public/loading.gif (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/src/log4j.properties (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/views/gwtorganise_view.html.php (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/.htaccess (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/META-INF/jdoconfig.xml (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/classes/log4j.properties (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/datanucleus-core-1.1.5.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/gwt-servlet.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/WEB-INF/lib/jdo2-api-2.3-eb.jar (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/G3viewer.css (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/clear.cache.gif (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/g3viewer.nocache.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gears_init.js (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/corner.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/corner_ie6.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/hborder.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/hborder_ie6.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/vborder.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/images/vborder_ie6.png (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/standard.css (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/gwt/standard/standard_rtl.css (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/hosted.html (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/g3viewer/loading.gif (100%) rename 3.1/modules/{gwtorganise => gwtorganize}/war/index.php (100%) diff --git a/3.0/modules/gwtorganise/.classpath b/3.0/modules/gwtorganize/.classpath similarity index 100% rename from 3.0/modules/gwtorganise/.classpath rename to 3.0/modules/gwtorganize/.classpath diff --git a/3.0/modules/gwtorganise/.externalToolBuilders/New_Builder.launch b/3.0/modules/gwtorganize/.externalToolBuilders/New_Builder.launch similarity index 100% rename from 3.0/modules/gwtorganise/.externalToolBuilders/New_Builder.launch rename to 3.0/modules/gwtorganize/.externalToolBuilders/New_Builder.launch diff --git a/3.0/modules/gwtorganise/.project b/3.0/modules/gwtorganize/.project similarity index 100% rename from 3.0/modules/gwtorganise/.project rename to 3.0/modules/gwtorganize/.project diff --git a/3.0/modules/gwtorganise/.settings/com.google.appengine.eclipse.core.prefs b/3.0/modules/gwtorganize/.settings/com.google.appengine.eclipse.core.prefs similarity index 100% rename from 3.0/modules/gwtorganise/.settings/com.google.appengine.eclipse.core.prefs rename to 3.0/modules/gwtorganize/.settings/com.google.appengine.eclipse.core.prefs diff --git a/3.0/modules/gwtorganise/.settings/com.google.gdt.eclipse.core.prefs b/3.0/modules/gwtorganize/.settings/com.google.gdt.eclipse.core.prefs similarity index 100% rename from 3.0/modules/gwtorganise/.settings/com.google.gdt.eclipse.core.prefs rename to 3.0/modules/gwtorganize/.settings/com.google.gdt.eclipse.core.prefs diff --git a/3.0/modules/gwtorganise/.settings/com.google.gwt.eclipse.core.prefs b/3.0/modules/gwtorganize/.settings/com.google.gwt.eclipse.core.prefs similarity index 100% rename from 3.0/modules/gwtorganise/.settings/com.google.gwt.eclipse.core.prefs rename to 3.0/modules/gwtorganize/.settings/com.google.gwt.eclipse.core.prefs diff --git a/3.0/modules/gwtorganise/build.xml b/3.0/modules/gwtorganize/build.xml similarity index 100% rename from 3.0/modules/gwtorganise/build.xml rename to 3.0/modules/gwtorganize/build.xml diff --git a/3.0/modules/gwtorganise/controllers/admin_gwtorganise.php b/3.0/modules/gwtorganize/controllers/admin_gwtorganise.php similarity index 100% rename from 3.0/modules/gwtorganise/controllers/admin_gwtorganise.php rename to 3.0/modules/gwtorganize/controllers/admin_gwtorganise.php diff --git a/3.0/modules/gwtorganise/controllers/admin_upload_configure.php b/3.0/modules/gwtorganize/controllers/admin_upload_configure.php similarity index 100% rename from 3.0/modules/gwtorganise/controllers/admin_upload_configure.php rename to 3.0/modules/gwtorganize/controllers/admin_upload_configure.php diff --git a/3.0/modules/gwtorganise/controllers/json_album.php b/3.0/modules/gwtorganize/controllers/json_album.php similarity index 100% rename from 3.0/modules/gwtorganise/controllers/json_album.php rename to 3.0/modules/gwtorganize/controllers/json_album.php diff --git a/3.0/modules/gwtorganise/helpers/gwtorganise_event.php b/3.0/modules/gwtorganize/helpers/gwtorganise_event.php similarity index 100% rename from 3.0/modules/gwtorganise/helpers/gwtorganise_event.php rename to 3.0/modules/gwtorganize/helpers/gwtorganise_event.php diff --git a/3.0/modules/gwtorganise/helpers/gwtorganise_installer.php b/3.0/modules/gwtorganize/helpers/gwtorganise_installer.php similarity index 100% rename from 3.0/modules/gwtorganise/helpers/gwtorganise_installer.php rename to 3.0/modules/gwtorganize/helpers/gwtorganise_installer.php diff --git a/3.0/modules/gwtorganise/helpers/revision.php b/3.0/modules/gwtorganize/helpers/revision.php similarity index 100% rename from 3.0/modules/gwtorganise/helpers/revision.php rename to 3.0/modules/gwtorganize/helpers/revision.php diff --git a/3.0/modules/gwtorganise/helpers/upload_configuration.php b/3.0/modules/gwtorganize/helpers/upload_configuration.php similarity index 100% rename from 3.0/modules/gwtorganise/helpers/upload_configuration.php rename to 3.0/modules/gwtorganize/helpers/upload_configuration.php diff --git a/3.0/modules/gwtorganise/module.info b/3.0/modules/gwtorganize/module.info similarity index 100% rename from 3.0/modules/gwtorganise/module.info rename to 3.0/modules/gwtorganize/module.info diff --git a/3.0/modules/gwtorganise/src/META-INF/jdoconfig.xml b/3.0/modules/gwtorganize/src/META-INF/jdoconfig.xml similarity index 100% rename from 3.0/modules/gwtorganise/src/META-INF/jdoconfig.xml rename to 3.0/modules/gwtorganize/src/META-INF/jdoconfig.xml diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/G3viewer.gwt.xml b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/G3viewer.gwt.xml similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/G3viewer.gwt.xml rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/G3viewer.gwt.xml diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Album.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Album.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Album.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Album.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTree.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTree.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTree.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTree.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncResizer.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncResizer.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncResizer.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncResizer.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncRunner.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncRunner.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncRunner.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncRunner.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/DropZoneController.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/DropZoneController.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/DropZoneController.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/DropZoneController.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/G3Viewer.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/G3Viewer.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/G3Viewer.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/G3Viewer.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogBox.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogBox.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogBox.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogBox.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogHandler.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogHandler.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogHandler.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogHandler.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/ImageDialogBox.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/ImageDialogBox.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/ImageDialogBox.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/ImageDialogBox.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/InformationBar.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/InformationBar.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/InformationBar.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/InformationBar.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Item.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Item.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Item.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Item.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseCallback.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseCallback.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseCallback.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseCallback.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Loading.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Loading.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Loading.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Loading.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/MyPickupDragController.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/MyPickupDragController.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/MyPickupDragController.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/MyPickupDragController.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/ResizeOptions.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/ResizeOptions.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/ResizeOptions.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/ResizeOptions.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControl.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControl.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControl.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControl.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControlNoGears.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControlNoGears.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControlNoGears.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControlNoGears.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadFile.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadFile.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadFile.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadFile.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Utils.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Utils.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/Utils.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/Utils.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/View.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/View.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/View.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/View.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Canvas.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Canvas.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Canvas.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Canvas.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Factory.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Factory.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Factory.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Factory.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/public/G3viewer.css b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/public/G3viewer.css similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/public/G3viewer.css rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/public/G3viewer.css diff --git a/3.0/modules/gwtorganise/src/com/gloopics/g3viewer/public/loading.gif b/3.0/modules/gwtorganize/src/com/gloopics/g3viewer/public/loading.gif similarity index 100% rename from 3.0/modules/gwtorganise/src/com/gloopics/g3viewer/public/loading.gif rename to 3.0/modules/gwtorganize/src/com/gloopics/g3viewer/public/loading.gif diff --git a/3.0/modules/gwtorganise/src/log4j.properties b/3.0/modules/gwtorganize/src/log4j.properties similarity index 100% rename from 3.0/modules/gwtorganise/src/log4j.properties rename to 3.0/modules/gwtorganize/src/log4j.properties diff --git a/3.0/modules/gwtorganise/views/gwtorganise_view.html.php b/3.0/modules/gwtorganize/views/gwtorganise_view.html.php similarity index 100% rename from 3.0/modules/gwtorganise/views/gwtorganise_view.html.php rename to 3.0/modules/gwtorganize/views/gwtorganise_view.html.php diff --git a/3.0/modules/gwtorganise/war/.htaccess b/3.0/modules/gwtorganize/war/.htaccess similarity index 100% rename from 3.0/modules/gwtorganise/war/.htaccess rename to 3.0/modules/gwtorganize/war/.htaccess diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/META-INF/jdoconfig.xml b/3.0/modules/gwtorganize/war/WEB-INF/classes/META-INF/jdoconfig.xml similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/META-INF/jdoconfig.xml rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/META-INF/jdoconfig.xml diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif b/3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif diff --git a/3.0/modules/gwtorganise/war/WEB-INF/classes/log4j.properties b/3.0/modules/gwtorganize/war/WEB-INF/classes/log4j.properties similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/classes/log4j.properties rename to 3.0/modules/gwtorganize/war/WEB-INF/classes/log4j.properties diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/datanucleus-core-1.1.5.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/datanucleus-core-1.1.5.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/datanucleus-core-1.1.5.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/datanucleus-core-1.1.5.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/gwt-servlet.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/gwt-servlet.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/gwt-servlet.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/gwt-servlet.jar diff --git a/3.0/modules/gwtorganise/war/WEB-INF/lib/jdo2-api-2.3-eb.jar b/3.0/modules/gwtorganize/war/WEB-INF/lib/jdo2-api-2.3-eb.jar similarity index 100% rename from 3.0/modules/gwtorganise/war/WEB-INF/lib/jdo2-api-2.3-eb.jar rename to 3.0/modules/gwtorganize/war/WEB-INF/lib/jdo2-api-2.3-eb.jar diff --git a/3.0/modules/gwtorganise/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png b/3.0/modules/gwtorganize/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png rename to 3.0/modules/gwtorganize/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html b/3.0/modules/gwtorganize/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html b/3.0/modules/gwtorganize/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png b/3.0/modules/gwtorganize/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png rename to 3.0/modules/gwtorganize/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html b/3.0/modules/gwtorganize/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html b/3.0/modules/gwtorganize/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html b/3.0/modules/gwtorganize/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html b/3.0/modules/gwtorganize/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html b/3.0/modules/gwtorganize/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png b/3.0/modules/gwtorganize/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png rename to 3.0/modules/gwtorganize/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html b/3.0/modules/gwtorganize/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html b/3.0/modules/gwtorganize/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html b/3.0/modules/gwtorganize/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html b/3.0/modules/gwtorganize/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html b/3.0/modules/gwtorganize/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html rename to 3.0/modules/gwtorganize/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png b/3.0/modules/gwtorganize/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png rename to 3.0/modules/gwtorganize/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png b/3.0/modules/gwtorganize/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png rename to 3.0/modules/gwtorganize/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png b/3.0/modules/gwtorganize/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png rename to 3.0/modules/gwtorganize/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/G3viewer.css b/3.0/modules/gwtorganize/war/g3viewer/G3viewer.css similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/G3viewer.css rename to 3.0/modules/gwtorganize/war/g3viewer/G3viewer.css diff --git a/3.0/modules/gwtorganise/war/g3viewer/clear.cache.gif b/3.0/modules/gwtorganize/war/g3viewer/clear.cache.gif similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/clear.cache.gif rename to 3.0/modules/gwtorganize/war/g3viewer/clear.cache.gif diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js b/3.0/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js rename to 3.0/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/g3viewer.nocache.js b/3.0/modules/gwtorganize/war/g3viewer/g3viewer.nocache.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/g3viewer.nocache.js rename to 3.0/modules/gwtorganize/war/g3viewer/g3viewer.nocache.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/gears_init.js b/3.0/modules/gwtorganize/war/g3viewer/gears_init.js similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gears_init.js rename to 3.0/modules/gwtorganize/war/g3viewer/gears_init.js diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner_ie6.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner_ie6.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner_ie6.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner_ie6.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder_ie6.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder_ie6.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder_ie6.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder_ie6.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder_ie6.png b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder_ie6.png similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder_ie6.png rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder_ie6.png diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/standard.css b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/standard.css similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/standard.css rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/standard.css diff --git a/3.0/modules/gwtorganise/war/g3viewer/gwt/standard/standard_rtl.css b/3.0/modules/gwtorganize/war/g3viewer/gwt/standard/standard_rtl.css similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/gwt/standard/standard_rtl.css rename to 3.0/modules/gwtorganize/war/g3viewer/gwt/standard/standard_rtl.css diff --git a/3.0/modules/gwtorganise/war/g3viewer/hosted.html b/3.0/modules/gwtorganize/war/g3viewer/hosted.html similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/hosted.html rename to 3.0/modules/gwtorganize/war/g3viewer/hosted.html diff --git a/3.0/modules/gwtorganise/war/g3viewer/loading.gif b/3.0/modules/gwtorganize/war/g3viewer/loading.gif similarity index 100% rename from 3.0/modules/gwtorganise/war/g3viewer/loading.gif rename to 3.0/modules/gwtorganize/war/g3viewer/loading.gif diff --git a/3.0/modules/gwtorganise/war/index.php b/3.0/modules/gwtorganize/war/index.php similarity index 100% rename from 3.0/modules/gwtorganise/war/index.php rename to 3.0/modules/gwtorganize/war/index.php diff --git a/3.1/modules/gwtorganise/.classpath b/3.1/modules/gwtorganize/.classpath similarity index 100% rename from 3.1/modules/gwtorganise/.classpath rename to 3.1/modules/gwtorganize/.classpath diff --git a/3.1/modules/gwtorganise/.externalToolBuilders/New_Builder.launch b/3.1/modules/gwtorganize/.externalToolBuilders/New_Builder.launch similarity index 100% rename from 3.1/modules/gwtorganise/.externalToolBuilders/New_Builder.launch rename to 3.1/modules/gwtorganize/.externalToolBuilders/New_Builder.launch diff --git a/3.1/modules/gwtorganise/.project b/3.1/modules/gwtorganize/.project similarity index 100% rename from 3.1/modules/gwtorganise/.project rename to 3.1/modules/gwtorganize/.project diff --git a/3.1/modules/gwtorganise/.settings/com.google.appengine.eclipse.core.prefs b/3.1/modules/gwtorganize/.settings/com.google.appengine.eclipse.core.prefs similarity index 100% rename from 3.1/modules/gwtorganise/.settings/com.google.appengine.eclipse.core.prefs rename to 3.1/modules/gwtorganize/.settings/com.google.appengine.eclipse.core.prefs diff --git a/3.1/modules/gwtorganise/.settings/com.google.gdt.eclipse.core.prefs b/3.1/modules/gwtorganize/.settings/com.google.gdt.eclipse.core.prefs similarity index 100% rename from 3.1/modules/gwtorganise/.settings/com.google.gdt.eclipse.core.prefs rename to 3.1/modules/gwtorganize/.settings/com.google.gdt.eclipse.core.prefs diff --git a/3.1/modules/gwtorganise/.settings/com.google.gwt.eclipse.core.prefs b/3.1/modules/gwtorganize/.settings/com.google.gwt.eclipse.core.prefs similarity index 100% rename from 3.1/modules/gwtorganise/.settings/com.google.gwt.eclipse.core.prefs rename to 3.1/modules/gwtorganize/.settings/com.google.gwt.eclipse.core.prefs diff --git a/3.1/modules/gwtorganise/build.xml b/3.1/modules/gwtorganize/build.xml similarity index 100% rename from 3.1/modules/gwtorganise/build.xml rename to 3.1/modules/gwtorganize/build.xml diff --git a/3.1/modules/gwtorganise/controllers/admin_gwtorganise.php b/3.1/modules/gwtorganize/controllers/admin_gwtorganise.php similarity index 100% rename from 3.1/modules/gwtorganise/controllers/admin_gwtorganise.php rename to 3.1/modules/gwtorganize/controllers/admin_gwtorganise.php diff --git a/3.1/modules/gwtorganise/controllers/admin_upload_configure.php b/3.1/modules/gwtorganize/controllers/admin_upload_configure.php similarity index 100% rename from 3.1/modules/gwtorganise/controllers/admin_upload_configure.php rename to 3.1/modules/gwtorganize/controllers/admin_upload_configure.php diff --git a/3.1/modules/gwtorganise/controllers/json_album.php b/3.1/modules/gwtorganize/controllers/json_album.php similarity index 100% rename from 3.1/modules/gwtorganise/controllers/json_album.php rename to 3.1/modules/gwtorganize/controllers/json_album.php diff --git a/3.1/modules/gwtorganise/helpers/gwtorganise_event.php b/3.1/modules/gwtorganize/helpers/gwtorganise_event.php similarity index 100% rename from 3.1/modules/gwtorganise/helpers/gwtorganise_event.php rename to 3.1/modules/gwtorganize/helpers/gwtorganise_event.php diff --git a/3.1/modules/gwtorganise/helpers/gwtorganise_installer.php b/3.1/modules/gwtorganize/helpers/gwtorganise_installer.php similarity index 100% rename from 3.1/modules/gwtorganise/helpers/gwtorganise_installer.php rename to 3.1/modules/gwtorganize/helpers/gwtorganise_installer.php diff --git a/3.1/modules/gwtorganise/helpers/revision.php b/3.1/modules/gwtorganize/helpers/revision.php similarity index 100% rename from 3.1/modules/gwtorganise/helpers/revision.php rename to 3.1/modules/gwtorganize/helpers/revision.php diff --git a/3.1/modules/gwtorganise/helpers/upload_configuration.php b/3.1/modules/gwtorganize/helpers/upload_configuration.php similarity index 100% rename from 3.1/modules/gwtorganise/helpers/upload_configuration.php rename to 3.1/modules/gwtorganize/helpers/upload_configuration.php diff --git a/3.1/modules/gwtorganise/module.info b/3.1/modules/gwtorganize/module.info similarity index 100% rename from 3.1/modules/gwtorganise/module.info rename to 3.1/modules/gwtorganize/module.info diff --git a/3.1/modules/gwtorganise/src/META-INF/jdoconfig.xml b/3.1/modules/gwtorganize/src/META-INF/jdoconfig.xml similarity index 100% rename from 3.1/modules/gwtorganise/src/META-INF/jdoconfig.xml rename to 3.1/modules/gwtorganize/src/META-INF/jdoconfig.xml diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/G3viewer.gwt.xml b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/G3viewer.gwt.xml similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/G3viewer.gwt.xml rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/G3viewer.gwt.xml diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Album.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Album.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Album.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Album.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumItemDropContainer.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTree.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTree.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTree.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTree.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AlbumTreeDropController.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncResizer.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncResizer.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncResizer.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncResizer.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncRunner.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncRunner.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/AsyncRunner.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/AsyncRunner.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/ConfirmDialogBox.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/DropZoneController.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/DropZoneController.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/DropZoneController.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/DropZoneController.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/G3Viewer.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/G3Viewer.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/G3Viewer.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/G3Viewer.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogBox.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogBox.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogBox.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogBox.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogHandler.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogHandler.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpDialogHandler.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpDialogHandler.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/HttpSuccessHandler.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/ImageDialogBox.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/ImageDialogBox.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/ImageDialogBox.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/ImageDialogBox.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/InformationBar.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/InformationBar.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/InformationBar.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/InformationBar.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Item.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Item.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Item.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Item.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseCallback.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseCallback.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseCallback.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseCallback.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/JSONResponseTextHandler.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Loading.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Loading.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Loading.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Loading.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/MyPickupDragController.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/MyPickupDragController.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/MyPickupDragController.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/MyPickupDragController.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/ResizeOptions.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/ResizeOptions.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/ResizeOptions.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/ResizeOptions.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControl.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControl.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControl.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControl.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControlNoGears.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControlNoGears.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadControlNoGears.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadControlNoGears.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadFile.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadFile.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/UploadFile.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/UploadFile.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Utils.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Utils.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/Utils.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/Utils.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/View.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/View.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/View.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/View.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Canvas.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Canvas.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Canvas.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Canvas.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Factory.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Factory.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/Factory.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/Factory.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/canvas/ResizeFilter.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.java diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/public/G3viewer.css b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/public/G3viewer.css similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/public/G3viewer.css rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/public/G3viewer.css diff --git a/3.1/modules/gwtorganise/src/com/gloopics/g3viewer/public/loading.gif b/3.1/modules/gwtorganize/src/com/gloopics/g3viewer/public/loading.gif similarity index 100% rename from 3.1/modules/gwtorganise/src/com/gloopics/g3viewer/public/loading.gif rename to 3.1/modules/gwtorganize/src/com/gloopics/g3viewer/public/loading.gif diff --git a/3.1/modules/gwtorganise/src/log4j.properties b/3.1/modules/gwtorganize/src/log4j.properties similarity index 100% rename from 3.1/modules/gwtorganise/src/log4j.properties rename to 3.1/modules/gwtorganize/src/log4j.properties diff --git a/3.1/modules/gwtorganise/views/gwtorganise_view.html.php b/3.1/modules/gwtorganize/views/gwtorganise_view.html.php similarity index 100% rename from 3.1/modules/gwtorganise/views/gwtorganise_view.html.php rename to 3.1/modules/gwtorganize/views/gwtorganise_view.html.php diff --git a/3.1/modules/gwtorganise/war/.htaccess b/3.1/modules/gwtorganize/war/.htaccess similarity index 100% rename from 3.1/modules/gwtorganise/war/.htaccess rename to 3.1/modules/gwtorganize/war/.htaccess diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/META-INF/jdoconfig.xml b/3.1/modules/gwtorganize/war/WEB-INF/classes/META-INF/jdoconfig.xml similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/META-INF/jdoconfig.xml rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/META-INF/jdoconfig.xml diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/G3viewer.gwt.xml diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$10.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$11.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$12.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$3.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$4.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$5.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$6.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$7.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$8.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album$9.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Album.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumItemDropContainer.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTree.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AlbumTreeDropController.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncResizer.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/AsyncRunner.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox$ConfirmCallBack.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ConfirmDialogBox.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/DropZoneController.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$ErrorDialog.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer$SimplePanelEx.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/G3Viewer.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$3.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$4.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$5.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox$RequestCallbackImpl.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogBox.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpDialogHandler.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/HttpSuccessHandler.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox$3.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ImageDialogBox.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/InformationBar.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$3.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$4.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$5.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$6.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$7.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$8.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item$9.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Item.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseCallback.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/JSONResponseTextHandler.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Loading.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/MyPickupDragController.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/ResizeOptions.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControl.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadControlNoGears.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$3.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile$ProgressBar.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/UploadFile.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/Utils.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View$3.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/View.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Canvas.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/Factory.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter$2.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/canvas/ResizeFilter.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDrop.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropBase.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFile.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDropFileIE.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DesktopDroppableWidget.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactory.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryIE.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears$1.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/client/dnddesktop/DndDesktopFactoryNoGears.class diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/G3viewer.css diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif b/3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/com/gloopics/g3viewer/public/loading.gif diff --git a/3.1/modules/gwtorganise/war/WEB-INF/classes/log4j.properties b/3.1/modules/gwtorganize/war/WEB-INF/classes/log4j.properties similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/classes/log4j.properties rename to 3.1/modules/gwtorganize/war/WEB-INF/classes/log4j.properties diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/appengine-api-1.0-sdk-1.2.5.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/appengine-api-labs-1.2.5.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/datanucleus-appengine-1.0.3.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/datanucleus-core-1.1.5.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/datanucleus-core-1.1.5.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/datanucleus-core-1.1.5.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/datanucleus-core-1.1.5.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/datanucleus-jpa-1.1.5.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/gwt-servlet.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/gwt-servlet.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/gwt-servlet.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/gwt-servlet.jar diff --git a/3.1/modules/gwtorganise/war/WEB-INF/lib/jdo2-api-2.3-eb.jar b/3.1/modules/gwtorganize/war/WEB-INF/lib/jdo2-api-2.3-eb.jar similarity index 100% rename from 3.1/modules/gwtorganise/war/WEB-INF/lib/jdo2-api-2.3-eb.jar rename to 3.1/modules/gwtorganize/war/WEB-INF/lib/jdo2-api-2.3-eb.jar diff --git a/3.1/modules/gwtorganise/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png b/3.1/modules/gwtorganize/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png rename to 3.1/modules/gwtorganize/war/g3viewer/0A9476898799A150D840F0B1C3672921.cache.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html b/3.1/modules/gwtorganize/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/0D97DF37194D1924CC80394AAA96B9A3.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html b/3.1/modules/gwtorganize/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/27AC86F0820D8F960DBF73C151C0332B.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png b/3.1/modules/gwtorganize/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png rename to 3.1/modules/gwtorganize/war/g3viewer/396F806CD63ABD414BFBB9D57429F05B.cache.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html b/3.1/modules/gwtorganize/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/4AFE598FDFDF189DD61F57E554328B10.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html b/3.1/modules/gwtorganize/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/4E8EC2279CB4B46228EFF0682ED166A4.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html b/3.1/modules/gwtorganize/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/4F7AD7D8299143D876CB4071BE00BF02.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html b/3.1/modules/gwtorganize/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/6462B363383D23B8418857B7A6FAD85B.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html b/3.1/modules/gwtorganize/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/71ED95F3DFB964762667E45E2922704D.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png b/3.1/modules/gwtorganize/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png rename to 3.1/modules/gwtorganize/war/g3viewer/8603379B5088782D2C0620FAE856E112.cache.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html b/3.1/modules/gwtorganize/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/884CB866FECF37EDDE4914CA60AF2511.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html b/3.1/modules/gwtorganize/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/9DC95FB4BEC084EF810751F04B440FD7.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html b/3.1/modules/gwtorganize/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/A8FBB0ADAFEE7F8EA1CDB15765D13A7F.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html b/3.1/modules/gwtorganize/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/CE15F73DB4EDED1CF8F93F95A728792D.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html b/3.1/modules/gwtorganize/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html rename to 3.1/modules/gwtorganize/war/g3viewer/D096B0ED44CBABF1A6B1F2C2D31F4FCC.cache.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png b/3.1/modules/gwtorganize/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png rename to 3.1/modules/gwtorganize/war/g3viewer/DF7764EEC1903CD03C9545B354D8D8E4.cache.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png b/3.1/modules/gwtorganize/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png rename to 3.1/modules/gwtorganize/war/g3viewer/E44767377485D18D6B6864F65BA8EF73.cache.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png b/3.1/modules/gwtorganize/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png rename to 3.1/modules/gwtorganize/war/g3viewer/EDC7827FEEA59EE44AD790B1C6430C45.cache.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/G3viewer.css b/3.1/modules/gwtorganize/war/g3viewer/G3viewer.css similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/G3viewer.css rename to 3.1/modules/gwtorganize/war/g3viewer/G3viewer.css diff --git a/3.1/modules/gwtorganise/war/g3viewer/clear.cache.gif b/3.1/modules/gwtorganize/war/g3viewer/clear.cache.gif similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/clear.cache.gif rename to 3.1/modules/gwtorganize/war/g3viewer/clear.cache.gif diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/0D97DF37194D1924CC80394AAA96B9A3/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/27AC86F0820D8F960DBF73C151C0332B/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4AFE598FDFDF189DD61F57E554328B10/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4E8EC2279CB4B46228EFF0682ED166A4/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/4F7AD7D8299143D876CB4071BE00BF02/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/6462B363383D23B8418857B7A6FAD85B/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/71ED95F3DFB964762667E45E2922704D/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/884CB866FECF37EDDE4914CA60AF2511/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/9DC95FB4BEC084EF810751F04B440FD7/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/A8FBB0ADAFEE7F8EA1CDB15765D13A7F/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/CE15F73DB4EDED1CF8F93F95A728792D/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/1.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/2.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js b/3.1/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js rename to 3.1/modules/gwtorganize/war/g3viewer/deferredjs/D096B0ED44CBABF1A6B1F2C2D31F4FCC/3.cache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/g3viewer.nocache.js b/3.1/modules/gwtorganize/war/g3viewer/g3viewer.nocache.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/g3viewer.nocache.js rename to 3.1/modules/gwtorganize/war/g3viewer/g3viewer.nocache.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/gears_init.js b/3.1/modules/gwtorganize/war/g3viewer/gears_init.js similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gears_init.js rename to 3.1/modules/gwtorganize/war/g3viewer/gears_init.js diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner_ie6.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner_ie6.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/corner_ie6.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/corner_ie6.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder_ie6.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder_ie6.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/hborder_ie6.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/hborder_ie6.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topleft.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/corner_dialog_topright.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_blue_shadow.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/hborder_gray_shadow.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_blue_shadow.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/ie6/vborder_gray_shadow.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder_ie6.png b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder_ie6.png similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/images/vborder_ie6.png rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/images/vborder_ie6.png diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/standard.css b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/standard.css similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/standard.css rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/standard.css diff --git a/3.1/modules/gwtorganise/war/g3viewer/gwt/standard/standard_rtl.css b/3.1/modules/gwtorganize/war/g3viewer/gwt/standard/standard_rtl.css similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/gwt/standard/standard_rtl.css rename to 3.1/modules/gwtorganize/war/g3viewer/gwt/standard/standard_rtl.css diff --git a/3.1/modules/gwtorganise/war/g3viewer/hosted.html b/3.1/modules/gwtorganize/war/g3viewer/hosted.html similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/hosted.html rename to 3.1/modules/gwtorganize/war/g3viewer/hosted.html diff --git a/3.1/modules/gwtorganise/war/g3viewer/loading.gif b/3.1/modules/gwtorganize/war/g3viewer/loading.gif similarity index 100% rename from 3.1/modules/gwtorganise/war/g3viewer/loading.gif rename to 3.1/modules/gwtorganize/war/g3viewer/loading.gif diff --git a/3.1/modules/gwtorganise/war/index.php b/3.1/modules/gwtorganize/war/index.php similarity index 100% rename from 3.1/modules/gwtorganise/war/index.php rename to 3.1/modules/gwtorganize/war/index.php