1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36:
37: $plugin_is_filter = 800 | ADMIN_PLUGIN | THEME_PLUGIN;
38: $plugin_description = gettext("Plugin to generate file download lists.");
39: $plugin_author = "Malte Müller (acrylian), Stephen Billard (sbillard)";
40:
41: $option_interface = "downloadList";
42:
43: zp_register_filter('admin_utilities_buttons', 'DownloadList::button');
44:
45: 46: 47: 48:
49: class DownloadList {
50:
51: function __construct() {
52: setOptionDefault('downloadList_directory', UPLOAD_FOLDER);
53: setOptionDefault('downloadList_showfilesize', 1);
54: setOptionDefault('downloadList_showdownloadcounter', 1);
55: setOptionDefault('downloadList_user', NULL);
56: setOptionDefault('downloadList_password', getOption('downloadList_pass'));
57: setOptionDefault('downloadList_hint', NULL);
58: setOptionDefault('downloadList_rights', NULL);
59: setOptionDefault('downloadList_zipFromCache', 0);
60: }
61:
62: function getOptionsSupported() {
63: $options = array(gettext('Download directory') => array('key' => 'downloadList_directory', 'type' => OPTION_TYPE_TEXTBOX,
64: 'order' => 2,
65: 'desc' => gettext("This download folder can be relative to your Zenphoto installation (<em>foldername</em>) or external to it (<em>../foldername</em>)! You can override this setting by using the parameter of the printdownloadList() directly on calling.")),
66: gettext('Show filesize of download items') => array('key' => 'downloadList_showfilesize', 'type' => OPTION_TYPE_CHECKBOX,
67: 'order' => 3,
68: 'desc' => ''),
69: gettext('Show download counter of download items') => array('key' => 'downloadList_showdownloadcounter', 'type' => OPTION_TYPE_CHECKBOX,
70: 'order' => 4,
71: 'desc' => ''),
72: gettext('Files to exclude from the download list') => array('key' => 'downloadList_excludesuffixes', 'type' => OPTION_TYPE_TEXTBOX,
73: 'order' => 5,
74: 'desc' => gettext('A list of file suffixes to exclude. Separate with comma and omit the dot (e.g "jpg").')),
75: gettext('Zip source') => array('key' => 'downloadList_zipFromCache', 'type' => OPTION_TYPE_RADIO,
76: 'order' => 6,
77: 'buttons' => array(gettext('From album') => 0, gettext('From Cache') => 1),
78: 'desc' => gettext('Make the album zip from the album folder or from the sized images in the cache.')),
79: gettext('User rights') => array('key' => 'downloadList_rights', 'type' => OPTION_TYPE_CHECKBOX,
80: 'order' => 1,
81: 'desc' => gettext('Check if users are required to have <em>file</em> rights to download.'))
82: );
83: if (GALLERY_SECURITY == 'public') {
84: $options[gettext('credentials')] = array('key' => 'downloadList_credentials', 'type' => OPTION_TYPE_CUSTOM,
85: 'order' => 0,
86: 'desc' => gettext('Provide credentials to password protect downloads'));
87: }
88: return $options;
89: }
90:
91: function handleOption($option, $currentValue) {
92: $user = getOption('downloadList_user');
93: $x = getOption('downloadList_password');
94: $hint = getOption('downloadList_hint');
95: ?>
96: <input type="hidden" name="password_enabled_downloadList" id="password_enabled_downloadList" value="0" />
97: <p class="password_downloadListextrashow">
98: <a href="javascript:toggle_passwords('_downloadList',true);">
99: <?php echo gettext("Password:"); ?>
100: </a>
101: <?php
102: if (empty($x)) {
103: ?>
104: <img src="<?php echo WEBPATH . '/' . ZENFOLDER; ?>/images/lock_open.png" alt="" class="icon-postiion-top8" />
105: <?php
106: } else {
107: $x = ' ';
108: ?>
109: <a onclick="resetPass('_downloadList');" title="<?php echo gettext('clear password'); ?>"><img src="<?php echo WEBPATH . '/' . ZENFOLDER; ?>/images/lock.png" alt="" class="icon-postiion-top8" /></a>
110: <?php
111: }
112: ?>
113: </p>
114: <div class="password_downloadListextrahide" style="display:none">
115: <a href="javascript:toggle_passwords('_downloadList',false);">
116: <?php echo gettext("Guest user:"); ?>
117: </a>
118: <br />
119: <input type="text" size="27" id="user_name_downloadList" name="user_downloadList"
120: onkeydown="passwordClear('_downloadList');"
121: value="<?php echo html_encode($user); ?>" />
122: <br />
123: <span id="strength_downloadList"><?php echo gettext("Password:"); ?></span>
124: <br />
125: <input type="password" size="27"
126: id="pass_downloadList" name="pass_downloadList"
127: onkeydown="passwordClear('_downloadList');"
128: onkeyup="passwordStrength('_downloadList');"
129: value="<?php echo $x; ?>" />
130: <label><input type="checkbox" name="disclose_password_downloadList" id="disclose_password_downloadList" onclick="passwordClear('_downloadList');
131: togglePassword('_downloadList');"><?php echo gettext('Show password'); ?></label>
132: <br />
133: <span class="password_field__downloadList">
134: <span id="match_downloadList"><?php echo gettext("(repeat)"); ?></span>
135: <br />
136: <input type="password" size="27"
137: id="pass_r_downloadList" name="pass_r_downloadList" disabled="disabled"
138: onkeydown="passwordClear('_downloadList');"
139: onkeyup="passwordMatch('_downloadList');"
140: value="<?php echo $x; ?>" />
141: <br />
142: </span>
143: <?php echo gettext("Password hint:"); ?>
144: <br />
145: <?php print_language_string_list($hint, 'hint_downloadList', false, NULL, 'hint_downloadList', 27); ?>
146: </div>
147: <?php
148: }
149:
150: static function handleOptionSave($themename, $themealbum) {
151: $notify = processCredentials('downloadList', '_downloadList');
152: if ($notify == '?mismatch=user') {
153: return '&custom=' . gettext('You must supply a password for the DownloadList user');
154: } else if ($notify) {
155: return '&custom=' . gettext('Your DownloadList passwords were empty or did not match');
156: }
157: return false;
158: }
159:
160: 161: 162: 163: 164:
165: static function updateListItemCount($path) {
166: $checkitem = query_single_row("SELECT `data` FROM " . prefix('plugin_storage') . " WHERE `aux` = " . db_quote($path) . " AND `type` = 'downloadList'");
167: if ($checkitem) {
168: $downloadcount = $checkitem['data'] + 1;
169: query("UPDATE " . prefix('plugin_storage') . " SET `data` = " . $downloadcount . ", `type` = 'downloadList' WHERE `aux` = " . db_quote($path) . " AND `type` = 'downloadList'");
170: }
171: }
172:
173: 174: 175: 176:
177: static function addListItem($path) {
178: $checkitem = query_single_row("SELECT `data` FROM " . prefix('plugin_storage') . " WHERE `aux` = " . db_quote($path) . " AND `type` = 'downloadList'");
179: if (!$checkitem) {
180: query("INSERT INTO " . prefix('plugin_storage') . " (`type`,`aux`,`data`) VALUES ('downloadList'," . db_quote($path) . ",'0')");
181: }
182: zp_apply_filter('downloadlist_processdownload',$path);
183: }
184:
185: 186: 187:
188:
189: static function getListItemsFromDB() {
190: $downloaditems = query_full_array("SELECT id, `aux`, `data` FROM " . prefix('plugin_storage') . " WHERE `type` = 'downloadList'");
191: return $downloaditems;
192: }
193:
194: 195: 196:
197:
198: static function getListItemFromDB($file) {
199: $downloaditem = query_single_row($sql = "SELECT id, `aux`, `data` FROM " . prefix('plugin_storage') . " WHERE `type` = 'downloadList' AND `aux` = " . db_quote($file));
200: return $downloaditem;
201: }
202:
203: 204: 205: 206: 207:
208: static function getItemID($path) {
209: $downloaditem = query_single_row("SELECT id, `aux`, `data` FROM " . prefix('plugin_storage') . " WHERE `type` = 'downloadList' AND `aux` = " . db_quote($path));
210: if ($downloaditem) {
211: return $downloaditem['id'];
212: } else {
213: return false;
214: }
215: }
216:
217: 218: 219: 220: 221:
222: static function printListArray($array, $listtype = 'ol') {
223: if ($listtype != 'ol' || $listtype != 'ul') {
224: $listtype = 'ol';
225: }
226: $filesize = '';
227: foreach ($array as $key => $file) {
228: ?>
229: <li>
230: <?php
231: if (is_array($file)) {
232: echo $key;
233: echo '<' . $listtype . '>';
234: self::printListArray($file, $listtype);
235: echo '</' . $listtype . '>';
236: } else {
237: printDownloadURL($file);
238: }
239: ?>
240: </li>
241: <?php
242: }
243: }
244:
245: 246: 247:
248: static function button($buttons) {
249: $buttons[] = array(
250: 'category' => gettext('Info'),
251: 'enable' => true,
252: 'button_text' => gettext('Download statistics'),
253: 'formname' => 'downloadstatistics_button',
254: 'action' => WEBPATH . '/' . ZENFOLDER . '/' . PLUGIN_FOLDER . '/downloadList/download_statistics.php',
255: 'icon' => WEBPATH . '/' . ZENFOLDER . '/images/bar_graph.png',
256: 'title' => gettext('Counts of downloads'),
257: 'alt' => '',
258: 'hidden' => '',
259: 'rights' => ADMIN_RIGHTS,
260: );
261: return $buttons;
262: }
263:
264: static function noFile() {
265: global $_downloadFile;
266: if (TEST_RELEASE) {
267: $file = $_downloadFile;
268: } else {
269: $file = basename($_downloadFile);
270: }
271: ?>
272: <script type="text/javascript">
273:
274: window.onload = function() {
275: alert('<?php printf(gettext('File “%was not found.'), $file); ?>');
276: }
277:
278: </script>
279: <?php
280: }
281:
282: }
283:
284: class AlbumZip {
285:
286: 287: 288: 289: 290: 291: 292:
293: static function AddAlbum($album, $base, $filebase) {
294: global $_zp_zip_list, $zip_gallery;
295: $albumbase = substr($album->name, $base) . '/';
296: foreach ($album->sidecars as $suffix) {
297: $f = $albumbase . $album->name . '.' . $suffix;
298: if (file_exists(internalToFilesystem($f))) {
299: $_zp_zip_list[$filebase . $f] = $f;
300: }
301: }
302: $images = $album->getImages();
303: foreach ($images as $imagename) {
304: $image = newImage($album, $imagename);
305: $f = $albumbase . $image->filename;
306: $_zp_zip_list[$filebase . internalToFilesystem($f)] = $f;
307: $imagebase = stripSuffix($image->filename);
308: foreach ($image->sidecars as $suffix) {
309: $f = $albumbase . $imagebase . '.' . $suffix;
310: if (file_exists($f)) {
311: $_zp_zip_list[$filebase . $f] = $f;
312: }
313: }
314: }
315: $albums = $album->getAlbums();
316: foreach ($albums as $albumname) {
317: $subalbum = newAlbum($albumname);
318: if ($subalbum->exists && !$album->isDynamic()) {
319: self::AddAlbum($subalbum, $base, $filebase);
320: }
321: }
322: }
323:
324: 325: 326: 327: 328: 329: 330:
331: static function AddAlbumCache($album, $base, $filebase) {
332: global $_zp_zip_list, $zip_gallery, $defaultSize;
333: $albumbase = substr($album->name, $base) . '/';
334: $images = $album->getImages();
335: foreach ($images as $imagename) {
336: $image = newImage($album, $imagename);
337: $uri = $image->getSizedImage($defaultSize);
338: if (strpos($uri, 'i.php?') === false) {
339: $f = $albumbase . $image->filename;
340: $c = $albumbase . basename($uri);
341: $_zp_zip_list[$filebase . $c] = $f;
342: }
343: }
344: $albums = $album->getAlbums();
345: foreach ($albums as $albumname) {
346: $subalbum = newAlbum($albumname);
347: if ($subalbum->exists && !$album->isDynamic()) {
348: self::AddAlbumCache($subalbum, $base, $filebase);
349: }
350: }
351: }
352:
353: 354: 355: 356: 357: 358: 359:
360: static function pageError($err, $text) {
361: header("HTTP/1.0 " . $err . ' ' . $text);
362: header("Status: " . $err . ' ' . $text);
363: echo "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head> <title>" . $err . " - " . $text . "</TITLE> <META NAME=\"ROBOTS\" CONTENT=\"NOINDEX, FOLLOW\"></head>";
364: echo "<BODY bgcolor=\"#ffffff\" text=\"#000000\" link=\"#0000ff\" vlink=\"#0000ff\" alink=\"#0000ff\">";
365: echo "<FONT face=\"Helvitica,Arial,Sans-serif\" size=\"2\">";
366: echo "<b>" . sprintf(gettext('Page error: %2$s (%1$s)'), $err, $text) . "</b><br /><br />";
367: echo "</body></html>";
368: exitZP();
369: }
370:
371: 372: 373: 374: 375: 376:
377: static function create($albumname, $fromcache) {
378: global $_zp_zip_list, $_zp_gallery, $defaultSize;
379: $album = newAlbum($albumname);
380: if (!$album->isMyItem(LIST_RIGHTS) && !checkAlbumPassword($albumname)) {
381: self::pageError(403, gettext("Forbidden"));
382: }
383: if (!$album->exists) {
384: self::pageError(404, gettext('Album not found'));
385: }
386: $_zp_zip_list = array();
387: if ($fromcache) {
388: $opt = array('large_file_size' => 5 * 1024 * 1024, 'comment' => sprintf(gettext('Created from cached images of %1$s on %2$s.'), $album->name, zpFormattedDate(DATE_FORMAT, time())));
389: loadLocalOptions(false, $_zp_gallery->getCurrentTheme());
390: $defaultSize = getOption('image_size');
391: self::AddAlbumCache($album, strlen($albumname), SERVERPATH . '/' . CACHEFOLDER . '/' . $albumname);
392: } else {
393: $opt = array('large_file_size' => 5 * 1024 * 1024, 'comment' => sprintf(gettext('Created from images in %1$s on %2$s.'), $album->name, zpFormattedDate(DATE_FORMAT, time())));
394: self::AddAlbum($album, strlen($albumname), SERVERPATH . '/' . ALBUMFOLDER . '/' . $albumname);
395: }
396: $zip = new ZipStream($albumname . '.zip', $opt);
397: foreach ($_zp_zip_list as $path => $file) {
398: @set_time_limit(6000);
399: $zip->add_file_from_path(internalToFilesystem($file), internalToFilesystem($path));
400: }
401: $zip->finish();
402: }
403:
404:
405: }
406:
407: 408: 409: 410: 411: 412: 413: 414:
415: function printdownloadList($dir = '', $listtype = 'ol', $filters = array(), $excludesuffixes = '', $sort = 'desc') {
416: if ($listtype != 'ol' || $listtype != 'ul') {
417: $listtype = 'ol';
418: }
419: $files = getdownloadList($dir, $filters, $excludesuffixes, $sort);
420: echo '<' . $listtype . ' class="downloadList">';
421: DownloadList::printListArray($files, $listtype);
422: echo '</' . $listtype . '>';
423: }
424:
425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436:
437: function getdownloadList($dir8, $filters8, $excludesuffixes, $sort) {
438: $filters = Array('Thumbs.db');
439: foreach ($filters8 as $key => $file) {
440: $filters[$key] = internalToFilesystem($file);
441: }
442: if (empty($dir8)) {
443: $dir = SERVERPATH . '/' . getOption('downloadList_directory');
444: } else {
445: if (substr($dir8, 0, 1) == '/' || strpos($dir8, ':') !== false) {
446: $dir = internalToFilesystem($dir8);
447: } else {
448: $dir = SERVERPATH . '/' . internalToFilesystem($dir8);
449: }
450: }
451: if (empty($excludesuffixes)) {
452: $excludesuffixes = getOption('downloadList_excludesuffixes');
453: }
454: if (empty($excludesuffixes)) {
455: $excludesuffixes = array();
456: } elseif (!is_array($excludesuffixes)) {
457: $excludesuffixes = explode(',', $excludesuffixes);
458: }
459: if ($sort == 'asc') {
460: $direction = 0;
461: } else {
462: $direction = 1;
463: }
464: $dirs = array_diff(scandir($dir, $direction), $filters);
465: $dir_array = Array();
466: if ($sort == 'asc') {
467: natsort($dirs);
468: }
469: foreach ($dirs as $file) {
470: if (@$file{0} != '.') {
471: if (is_dir(internalToFilesystem($dir) . '/' . $file)) {
472: $dirN = filesystemToInternal($dir) . "/" . filesystemToInternal($file);
473: $dir_array[$file] = getdownloadList($dirN, $filters8, $excludesuffixes, $sort);
474: } else {
475: if (!in_array(getSuffix($file), $excludesuffixes)) {
476: $dir_array[$file] = $dir . '/' . filesystemToInternal($file);
477: }
478: }
479: }
480: }
481: return $dir_array;
482: }
483:
484: 485: 486: 487:
488: function getDownloadURL($file) {
489: if (substr($file, 0, 1) != '/' && strpos($file, ':') === false) {
490: $file = SERVERPATH . '/' . getOption('downloadList_directory') . '/' . $file;
491: }
492: $request = parse_url(getRequestURI());
493: if (isset($request['query'])) {
494: $query = parse_query($request['query']);
495: } else {
496: $query = array();
497: }
498: DownloadList::addListItem($file);
499: $link = '';
500: if ($id = DownloadList::getItemID($file)) {
501: $query['download'] = $id;
502: $link = FULLWEBPATH . '/' . preg_replace('~^' . WEBPATH . '/~', '', $request['path']) . '?' . http_build_query($query);
503: }
504: return $link;
505: }
506:
507: 508: 509: 510: 511:
512: function printDownloadURL($file, $linktext = NULL) {
513: if (substr($file, 0, 1) != '/' && strpos($file, ':') === false) {
514: $file = SERVERPATH . '/' . getOption('downloadList_directory') . '/' . $file;
515: }
516: $filesize = '';
517: if (getOption('downloadList_showfilesize')) {
518: $filesize = @filesize(internalToFilesystem($file));
519: $filesize = ' (' . byteConvert($filesize) . ')';
520: }
521: if (getOption('downloadList_showdownloadcounter')) {
522: $downloaditem = DownloadList::getListItemFromDB($file);
523: if ($downloaditem) {
524: $downloadcount = ' - ' . sprintf(ngettext('%u download', '%u downloads', $downloaditem['data']), $downloaditem['data']);
525: } else {
526: $downloadcount = ' - ' . gettext('0 downloads');
527: }
528: $filesize .= $downloadcount;
529: }
530: if (empty($linktext)) {
531: $filename = basename($file);
532: } else {
533: $filename = $linktext;
534: }
535: echo '<a href="' . html_encode(getDownloadURL($file)) . '" rel="nofollow" class="downloadlist_link">' . html_encode($filename) . '</a><small>' . $filesize . '</small>';
536: }
537:
538: 539: 540: 541: 542: 543: 544: 545: 546:
547: function printDownloadAlbumZipURL($linktext = NULL, $albumobj = NULL, $fromcache = NULL) {
548: global $_zp_current_album;
549: $request = parse_url(getRequestURI());
550: if (isset($request['query'])) {
551: $query = parse_query($request['query']);
552: } else {
553: $query = array();
554: }
555: if (is_null($albumobj)) {
556: $albumobj = $_zp_current_album;
557: }
558: if (!is_null($albumobj) && !$albumobj->isDynamic()) {
559: $file = $albumobj->name . '.zip';
560: DownloadList::addListItem($file);
561: if (getOption('downloadList_showdownloadcounter')) {
562: $downloaditem = DownloadList::getListItemFromDB($file);
563: if ($downloaditem) {
564: $downloadcount = ' - ' . sprintf(ngettext('%u download', '%u downloads', $downloaditem['data']), $downloaditem['data']);
565: } else {
566: $downloadcount = ' - ' . gettext('0 downloads');
567: }
568: $filesize = '<small>' . $downloadcount . '</small>';
569: } else {
570: $filesize = '';
571: }
572: if (!empty($linktext)) {
573: $file = $linktext;
574: }
575: $query['download'] = $albumobj->name;
576: $query['albumzip'] = 'true';
577: if ($fromcache) {
578: $query['fromcache'] = 'true';
579: }
580: $link = FULLWEBPATH . '/' . preg_replace('~^' . WEBPATH . '/~', '', $request['path']) . '?' . http_build_query($query);
581: echo '<a href="' . html_encode($link) . '" rel="nofollow" class="downloadlist_link">' . html_encode($file) . '</a>' . $filesize;
582: }
583: }
584:
585: 586: 587:
588: if (isset($_GET['download'])) {
589: $item = sanitize($_GET['download']);
590: if (empty($item) || !extensionEnabled('downloadList')) {
591: if (TEST_RELEASE) {
592: zp_error(gettext('Forbidden'));
593: } else {
594: header("HTTP/1.0 403 " . gettext("Forbidden"));
595: header("Status: 403 " . gettext("Forbidden"));
596: exitZP();
597: }
598: }
599: $hash = getOption('downloadList_password');
600: if (GALLERY_SECURITY != 'public' || $hash) {
601:
602: if(!zp_loggedin((getOption('downloadList_rights')) ? FILES_RIGHTS : ALL_RIGHTS)) {
603: $user = getOption('downloadList_user');
604: zp_handle_password('download_auth', $hash, $user);
605: if ((!empty($hash) && zp_getCookie('download_auth') != $hash)) {
606: $show = ($user) ? true : NULL;
607: $hint = '';
608: if (!empty($hash)) {
609: $hint = get_language_string(getOption('downloadList_hint'));
610: }
611: if (isset($_GET['albumzip'])) {
612: $item .= '&albumzip';
613: }
614: printPasswordForm($hint, true, $show, '?download=' . $item);
615: exitZP();
616: }
617: }
618: }
619: if (isset($_GET['albumzip'])) {
620: DownloadList::updateListItemCount($item . '.zip');
621: require_once(SERVERPATH . '/' . ZENFOLDER . '/lib-zipStream.php');
622: if (isset($_GET['fromcache'])) {
623: $fromcache = sanitize($isset($_GET['fromcache']));
624: } else {
625: $fromcache = getOption('downloadList_zipFromCache');
626: }
627: AlbumZip::create($item, $fromcache);
628: exitZP();
629: } else {
630: require_once(SERVERPATH . '/' . ZENFOLDER . '/lib-MimeTypes.php');
631: $item = (int) $item;
632: $path = query_single_row("SELECT `aux` FROM " . prefix('plugin_storage') . " WHERE id=" . $item);
633: $_downloadFile = internalToFilesystem($path['aux']);
634: if (file_exists($_downloadFile)) {
635: DownloadList::updateListItemCount($_downloadFile);
636: $ext = getSuffix($_downloadFile);
637: $mimetype = getMimeString($ext);
638: header('Content-Description: File Transfer');
639: header('Content-Type: ' . $mimetype);
640: header('Content-Disposition: attachment; filename=' . basename(urldecode($_downloadFile)));
641: header('Content-Transfer-Encoding: binary');
642: header('Expires: 0');
643: header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
644: header('Pragma: public');
645: header('Content-Length: ' . filesize($_downloadFile));
646: flush();
647: readfile($_downloadFile);
648: exitZP();
649: } else {
650: zp_register_filter('theme_body_open', 'DownloadList::noFile');
651: }
652: }
653: }
654: ?>