diff --git a/modules/downloadalbum/controllers/downloadalbum.php b/modules/downloadalbum/controllers/downloadalbum.php
new file mode 100644
index 00000000..83f4ab0c
--- /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)
+ 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
+ $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)
+ 0x81b40000, // external file attributes (4 bytes) => chmod 664
+ $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 00000000..ce7804d2
Binary files /dev/null and b/modules/downloadalbum/images/ico-view-downloadalbum.png differ
diff --git a/modules/downloadalbum/module.info b/modules/downloadalbum/module.info
new file mode 100644
index 00000000..177ad4a8
--- /dev/null
+++ b/modules/downloadalbum/module.info
@@ -0,0 +1,3 @@
+name = "DownloadAlbum"
+description = "Displays a link to download a ZIP archive of the current album."
+version = 1
diff --git a/modules/moduleupdates/controllers/admin_moduleupdates.php b/modules/moduleupdates/controllers/admin_moduleupdates.php
index fc9d1905..ba812515 100644
--- a/modules/moduleupdates/controllers/admin_moduleupdates.php
+++ b/modules/moduleupdates/controllers/admin_moduleupdates.php
@@ -47,16 +47,24 @@ class Admin_Moduleupdates_Controller extends Admin_Controller {
foreach (module::available() as $this_module_name => $module_info) {
+ $remote_version = '';
+ $remote_server = '';
+
list ($remote_version, $remote_server) = $this->get_remote_module_version($this_module_name);
$font_color = "black";
if ($remote_version == "DNE") {
$font_color = "blue";
- } else if ($remote_version < $module_info->code_version) {
+ } else if ($module_info->version != '' and $module_info->code_version < $module_info->version) {
+ $font_color = "pink";
+ } else if ($module_info->version != '' and $module_info->code_version > $module_info->version) {
+ $font_color = "orange";
+ } else if ($remote_version < $module_info->code_version or ($module_info->version != '' and $remote_version < $module_info->version)) {
$font_color = "green";
- } else if ($remote_version > $module_info->code_version) {
+ } else if ($remote_version > $module_info->code_version or ($module_info->version != '' and $remote_version > $module_info->version)) {
$font_color = "red";
}
+
$all_modules->$this_module_name = array ("name" => $module_info->name, "locked" => $module_info->locked,
"code_version" => $module_info->code_version, "active" => $module_info->active,
"version" => $module_info->version,"description" => $module_info->description,
@@ -91,7 +99,9 @@ class Admin_Moduleupdates_Controller extends Admin_Controller {
try {
$file = fopen ("http://github.com/gallery/gallery3/raw/master/modules/".$module_name."/module.info", "r");
- $server = '(G3)';
+ if ($file != null) {
+ $server = '(G3)';
+ }
}
catch (Exception $e) {
//echo 'Message: ' .$e->getMessage() . '
';
@@ -100,7 +110,9 @@ class Admin_Moduleupdates_Controller extends Admin_Controller {
if ($file == null) {
try {
$file = fopen ("http://github.com/gallery/gallery3-contrib/raw/master/modules/".$module_name."/module.info", "r");
- $server = '(G3CC)';
+ if ($file != null) {
+ $server = '(G3CC)';
+ }
}
catch (Exception $e) {
//echo 'Message: ' .$e->getMessage() . '
';
diff --git a/modules/moduleupdates/views/admin_moduleupdates.html.php b/modules/moduleupdates/views/admin_moduleupdates.html.php
index e4e3f038..8a7c6494 100644
--- a/modules/moduleupdates/views/admin_moduleupdates.html.php
+++ b/modules/moduleupdates/views/admin_moduleupdates.html.php
@@ -1,11 +1,13 @@
= t("Compares your installed module version against the ones stored in the GitHub.
") ?>
- = t("Red = Out of Date
") ?>
- = t("Green = Your version is newer
") ?>
+ = t("Red = Your version is older than the GitHub
") ?>
+ = t("Green = Your version is newer than the GitHub
") ?>
+ = t("Orange = Your file version is newer than the installed version
") ?>
+ = t("Pink = Your installed version is newer than file version
") ?>
= t("Blue = Does Not Exist/No information available
") ?>
= t("Module") ?> | -= t("Your Version") ?> | + = t("Your Version [File/Installed]") ?> |
= t("Remote Version") ?> | = t("Description") ?> |
---|---|---|---|---|
echo ""; ?> = $module_name['name'] ?> | -echo ""; ?> = $module_name['code_version'] ?> | +echo ""; ?> = $module_name['code_version'] ?> if ($module_name['version'] != '') echo "/".$module_name['version']; ?> | echo ""; ?> = $module_name['remote_version'] ?> = $module_name['remote_server'] ?> | echo ""; ?> = $module_name['description'] ?> |