1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: class Ansel_View_Upload
15: {
16: 17: 18: 19:
20: protected $_params;
21:
22: 23: 24: 25:
26: protected $_gallery;
27:
28: 29: 30: 31: 32:
33: protected $_forceNoScript = false;
34:
35: 36: 37: 38: 39:
40: protected $_haveCarousel = false;
41:
42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52:
53: public function __construct($params)
54: {
55: $this->_params = $params;
56: $this->_gallery = $this->_params['gallery'];
57: if (!empty($params['forceNoScript'])) {
58: $this->_forceNoScript = true;
59: }
60:
61: Ansel::initJSVariables();
62: Horde::addScriptFile('effects.js', 'horde', true);
63: Horde::addScriptFile('carousel.js', 'ansel', true);
64: Horde::addScriptFile('upload.js', 'ansel');
65: }
66:
67: public function run()
68: {
69:
70: $this->_handleFileUpload();
71:
72:
73: Horde::addScriptFile('plupload/plupload.js', 'horde');
74: Horde::addScriptFile('plupload/plupload.flash.js', 'horde');
75: Horde::addScriptFile('plupload/plupload.silverlight.js', 'horde');
76: Horde::addScriptFile('plupload/plupload.html5.js', 'horde');
77: Horde::addScriptFile('plupload/plupload.browserplus.js', 'horde');
78: Horde::addScriptFile('plupload/uploader.js', 'horde');
79:
80: $startText = _("Upload");
81: $addText = _("Add Images");
82: $header = _("Upload to gallery");
83: $returnText =_("View Gallery");
84: $subText = _("Add files to the upload queue and click the start button.");
85: $sizeError = _("File size error.");
86: $typeError = _("File type error.");
87:
88: $imple = $GLOBALS['injector']
89: ->getInstance('Horde_Core_Factory_Imple')
90: ->create(array('ansel', 'UploadNotification'));
91: $notificationUrl = (string)$imple->getUrl();
92: $this->_params['target']->add('gallery', $this->_params['gallery']->id);
93: $jsuri = $GLOBALS['registry']->get('jsuri', 'horde');
94:
95: if ($GLOBALS['browser']->getBrowser() == 'mozilla' && $GLOBALS['browser']->getMajor() <= 4) {
96: $multipart = 'true';
97: } else {
98: $multipart = 'false';
99: }
100: $js = <<< EOT
101: Ansel.ajax.uploadNotificationUrl = '{$notificationUrl}';
102: var uploader = new Horde_Uploader({
103: 'target': "{$this->_params['target']}",
104: drop_target: "{$this->_params['drop_target']}",
105: swf_path: '{$jsuri}/plupload/plupload.flash.swf',
106: xap_path: '{$jsuri}/plupload/plupload.silverlight.xap',
107: container: 'anseluploader',
108: text: { start: '{$startText}',
109: add: '{$addText}',
110: header: '{$header}',
111: returnButton: '{$returnText}',
112: subheader: '{$subText}',
113: size: '{$sizeError}',
114: type: '{$typeError}'
115: },
116: header_class: 'hordeUploaderHeader',
117: container_class: 'uploaderContainer',
118: return_target: '{$this->_params['return_target']}',
119: multipart: {$multipart}
120: },
121: {
122: 'uploadcomplete': function(up, files) {
123: Ansel.uploadedImages = files;
124: if (Ansel.conf.havetwitter) {
125: $('twitter').toggleClassName('hidden');
126: }
127: }
128: });
129: uploader.init();
130: $('twitter').observe('click', function() {
131: AnselUpload.doUploadNotification('twitter', '{$this->_gallery->id}');
132: });
133: EOT;
134:
135: $js .= $this->_doCarouselSetup();
136: Horde::addInlineScript($js, 'load');
137: }
138:
139: 140: 141:
142: public function handleLegacy()
143: {
144: global $conf, $notification, $browser;
145:
146: $vars = Horde_Variables::getDefaultVariables();
147: $form = new Ansel_Form_Upload($vars, _("Upload photos"));
148:
149:
150:
151: $js = $this->_doCarouselSetup();
152: if (!empty($js)) {
153: Horde::addInlineScript($js, 'load');
154: }
155:
156: if ($form->validate($vars)) {
157: $valid = true;
158: $uploaded = 0;
159: $form->getInfo($vars, $info);
160:
161:
162: $image_ids = array();
163: for ($i = 0; $i <= $conf['image']['num_uploads'] + 1; ++$i) {
164: if (empty($info['file' . $i]['file'])) {
165: continue;
166: }
167:
168: try {
169: $GLOBALS['browser']->wasFileUploaded('file' . $i);
170: } catch (Horde_Browser_Exception $e) {
171: if (!empty($info['file' . $i]['error'])) {
172: $notification->push(
173: sprintf(_("There was a problem uploading the photo: %s"), $info['file' . $i]['error']), 'horde.error');
174: } elseif (!filesize($info['file' . $i]['file'])) {
175: $notification->push(
176: _("The uploaded file appears to be empty. It may not exist on your computer."), 'horde.error');
177: }
178: $valid = false;
179: continue;
180: }
181:
182:
183: if (in_array(
184: $info['file' . $i]['type'],
185: array(
186: 'x-extension/zip',
187: 'application/x-compressed',
188: 'application/x-zip-compressed',
189: 'application/zip')
190: ) ||
191: Horde_Mime_Magic::filenameToMime($info['file' . $i]['name']) == 'application/zip') {
192:
193: $this->_handleZip($info['file' . $i]['name']);
194:
195: } else {
196:
197:
198: $data = file_get_contents($info['file' . $i]['file']);
199:
200:
201:
202: if (getimagesize($info['file' . $i]['file']) === false) {
203: $notification->push(
204: _("The file you uploaded does not appear to be a valid photo."),
205: 'horde.error');
206: continue;
207: }
208:
209:
210: $image_data = array(
211: 'image_filename' => $info['file' . $i]['name'],
212: 'image_caption' => $vars->get('image' . $i . '_desc'),
213: 'image_type' => $info['file' . $i]['type'],
214: 'data' => $data,
215: 'tags' => (isset($info['image' . $i . '_tags']) ? explode(',', $info['image' . $i . '_tags']) : array()));
216: try {
217: $image_ids[] = $this->_gallery->addImage(
218: $image_data, (bool)$vars->get('image' . $i . '_default'));
219: ++$uploaded;
220: } catch (Ansel_Exception $e) {
221: $notification->push(
222: sprintf(_("There was a problem saving the photo: %s"), $e->getMessage()),
223: 'horde.error');
224: $valid = false;
225: }
226: unset($data);
227: }
228: }
229:
230:
231: if ($uploaded) {
232: $cnt = count($image_ids);
233: for ($i = 0; $i < $conf['image']['autogen'] && $cnt > $i; $i++) {
234: $image_id = $image_ids[$i];
235: $image = $GLOBALS['injector']
236: ->getInstance('Ansel_Storage')
237: ->getImage($image_id);
238: $image->createView('screen');
239: $image->createView('thumb');
240: $image->createView('mini');
241: unset($image);
242: }
243:
244:
245: try {
246: Horde::callHook('postupload', array($image_ids), 'ansel');
247: } catch (Horde_Exception_HookNotSet $e) {}
248: $notification->push(sprintf(ngettext("%d photo was uploaded.", "%d photos were uploaded.", $uploaded), $uploaded), 'horde.success');
249: } elseif ($vars->get('submitbutton') != _("Cancel")) {
250: $notification->push(_("You did not select any photos to upload."), 'horde.error');
251: }
252:
253: if ($valid) {
254:
255: Ansel::getUrlFor(
256: 'view',
257: array(
258: 'gallery' => $this->_gallery->id,
259: 'slug' => $this->_gallery->get('slug'),
260: 'view' => 'Gallery',
261: 'page' => $page),
262: true)->redirect();
263: exit;
264: }
265: }
266:
267: Horde::startBuffer();
268: include ANSEL_TEMPLATES . '/image/upload.inc';
269:
270: return ($this->_forceNoScript ? '' : '<noscript>') . Horde::endBuffer() . ($this->_forceNoScript ? '' : '</noscript>');
271: }
272:
273: 274: 275: 276: 277:
278: protected function _doCarouselSetup()
279: {
280: if ($this->_haveCarousel) {
281: return '';
282: }
283:
284: $this->_haveCarousel = true;
285: $previewUrl = Horde::url('img/upload_preview.php')
286: ->add('gallery', $this->_gallery->id);
287:
288: $js = <<<EOT
289: Ajax.Response.prototype._getHeaderJSON = function() {
290: var nbElements = {$this->_gallery->countImages()};
291: var from = this.request.parameters.from;
292: var to = Math.min(nbElements, this.request.parameters.to);
293: return {html: this.responseText, from: from, to: to, more: to != nbElements};
294: }
295:
296: function runCarousel() {
297: updateCarouselSize();
298: carousel = new UI.Ajax.Carousel("horizontal_carousel", { url: "{$previewUrl->toString(true)}", elementSize: 115 })
299: .observe("request:started", function() {
300: $('spinner').show().morph("opacity:0.8", {duration:0.5});
301: })
302: .observe("request:ended", function() {
303: $('spinner').morph("opacity:0", {duration:0.5, afterFinish: function(obj) { obj.element.hide(); }});
304: });
305: }
306: function resized() {
307: updateCarouselSize();
308: if (carousel) {
309: carousel.updateSize();
310: }
311: }
312: function updateCarouselSize() {
313: var dim = $('anseluploader').getDimensions();
314: $("horizontal_carousel").style.width = dim.width + "px";
315: $$("#horizontal_carousel .container").first().style.width = (dim.width - 50) + "px";
316: }
317:
318: Event.observe(window, 'resize', resized);
319: carousel = null;
320: runCarousel();
321: EOT;
322:
323: return $js;
324: }
325:
326: 327: 328: 329:
330: protected function _handleFileUpload()
331: {
332: if ($filename = Horde_Util::getFormData('name')) {
333: if (isset($_SERVER["HTTP_CONTENT_TYPE"])) {
334: $type = $_SERVER["HTTP_CONTENT_TYPE"];
335: } elseif (isset($_SERVER["CONTENT_TYPE"])) {
336: $type = $_SERVER["CONTENT_TYPE"];
337: }
338:
339: if (empty($type) || $type == 'application/octet-stream') {
340: $temp = Horde_Util::getTempFile('', true);
341: $out = fopen($temp, 'wb');
342: if ($out) {
343:
344: $in = fopen("php://input", "rb");
345: if ($in) {
346: while ($buff = fread($in, 4096)) {
347: fwrite($out, $buff);
348: }
349: } else {
350: fclose($out);
351: header('Content-Type: application/json');
352: echo('{ "status" : "500", "file": "' . $temp. '", error" : { "message": "Failed to open input stream." } }');
353: exit;
354: }
355: } else {
356: header('Content-Type: application/json');
357: echo('{ "status" : "500", "file": "' . $temp. '", error" : { "message": "Failed to open output stream." } }');
358: exit;
359: }
360:
361:
362: if (!($type = Horde_Mime_Magic::analyzeFile($temp, isset($GLOBALS['conf']['mime']['magic_db']) ? $GLOBALS['conf']['mime']['magic_db'] : null))) {
363: $type = Horde_Mime_Magic::filenameToMime($filename);
364: }
365: } elseif (strpos($type, "multipart") !== false) {
366:
367: $temp = Horde_Util::getTempFile('', true);
368: $out = fopen($temp, 'wb');
369: if ($out) {
370: $in = fopen($_FILES['file']['tmp_name'], 'rb');
371: if ($in) {
372: while ($buff = fread($in, 4096)) {
373: fwrite($out, $buff);
374: }
375: } else {
376: fclose($out);
377: header('Content-Type: application/json');
378: echo('{ "status" : "500", "file": "' . $temp. '", error" : { "message": "Failed to open input stream." } }');
379: exit;
380: }
381: } else {
382: header('Content-Type: application/json');
383: echo('{ "status" : "500", "file": "' . $temp. '", error" : { "message": "Failed to open output stream." } }');
384: exit;
385: }
386: }
387:
388:
389: if (in_array(
390: $type,
391: array(
392: 'x-extension/zip',
393: 'application/x-compressed',
394: 'application/x-zip-compressed',
395: 'application/zip')) ||
396: Horde_Mime_Magic::filenameToMime($temp) == 'application/zip') {
397:
398:
399: try {
400: $image_ids = $this->_handleZip($temp);
401: } catch (Ansel_Exception $e) {
402: $notification->push(
403: sprintf(_("There was an error processing the uploaded archive: %s"), $e->getMessage()), 'horde.error');
404: }
405:
406: header('Content-Type: application/json');
407: echo('{ "status" : "200", "error" : {} }');
408: exit;
409: } else {
410:
411: $data = file_get_contents($temp);
412: if (getimagesize($temp) === false) {
413: header('Content-Type: application/json');
414: echo('{ "status" : "400", "error" : { "message": "Not a valid, supported image file." }, "id" : "id" }');
415: exit;
416: }
417:
418:
419: $image_data = array(
420: 'image_filename' => $filename,
421: 'image_type' => $type,
422: 'data' => $data);
423:
424: try {
425: $image_id = $this->_gallery->addImage($image_data);
426: $image_ids[] = $image_id;
427: } catch (Ansel_Exception $e) {
428: header('Content-Type: application/json');
429: echo('{ "status" : "400", "error" : { "message": "Not a valid, supported image file." }, "id" : "id" }');
430: exit;
431: }
432:
433: unset($data);
434: header('Content-Type: application/json');
435: echo('{ "status" : "200", "error" : {} }');
436: exit;
437: }
438: }
439: }
440:
441: 442: 443: 444: 445: 446: 447:
448: protected function _isMetaFile($filename)
449: {
450:
451: $len = strlen($filename);
452: if ($filename[$len - 1] == '/' ||
453: $filename == 'Thumbs.db' ||
454: strrpos($filename, '.DS_Store') == ($len - 9) ||
455: strrpos($filename, '.localized') == ($len - 10) ||
456: strpos($filename, '__MACOSX/') !== false) {
457:
458: return true;
459: }
460:
461: return false;
462: }
463:
464: 465: 466: 467: 468: 469: 470: 471:
472: private function _handleZip($filename)
473: {
474: $image_ids = array();
475:
476:
477: if (Horde_Util::extensionExists('zip')) {
478: $zip = new ZipArchive();
479: if ($zip->open($filename) !== true) {
480: throw new Ansel_Exception(_("Could not open zip archive."));
481: }
482:
483:
484: for ($z = 0; $z < $zip->numFiles; $z++) {
485: $zinfo = $zip->statIndex($z);
486: if ($this->_isMetaFile($zinfo['name'])) {
487: continue;
488: }
489:
490:
491: $stream = $zip->getStream($zinfo['name']);
492: $zdata = stream_get_contents($stream);
493: if (!strlen($zdata)) {
494: throw new Ansel_Exception(_("Could not extract image data from zip archive."));
495: }
496:
497:
498: $image_id = $this->_gallery->addImage(array('image_filename' => $zinfo['name'],
499: 'image_caption' => '',
500: 'data' => $zdata));
501: $image_ids[] = $image_id;
502: unset($zdata);
503: }
504: $zip->close();
505: unset($zip);
506: } else {
507:
508: $data = file_get_contents($filename);
509:
510:
511: try {
512: $zip = Horde_Compress::factory('zip');
513: $files = $zip->decompress($data, array('action' => Horde_Compress_Zip::ZIP_LIST));
514: } catch (Horde_Exception $e) {
515: throw new Ansel_Exception($e);
516: continue;
517: }
518:
519:
520: foreach ($files as $key => $zinfo) {
521: if ($this->_isMetaFile($zinfo['name'])) {
522: continue;
523: }
524:
525:
526: try {
527: $zdata = $zip->decompress($data,
528: array('action' => Horde_Compress_Zip::ZIP_DATA,
529: 'info' => $files,
530: 'key' => $key));
531: } catch (Horde_Exception $e) {
532: throw new Ansel_Exception($e);
533: }
534:
535:
536: $image_id = $this->_gallery->addImage(array('image_filename' => $zinfo['name'],
537: 'image_caption' => '',
538: 'data' => $zdata));
539: $image_ids[] = $image_id;
540: unset($zdata);
541: }
542: unset($zip);
543: unset($data);
544: }
545:
546: return $image_ids;
547: }
548:
549: }
550: