1<?php
2/*
3 * Gallery - a web based photo album viewer and editor
4 * Copyright (C) 2000-2008 Bharat Mediratta
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
19 */
20
21/**
22 * This edit plugin allows you to customize the photo's thumbnail
23 * @package GalleryCore
24 * @subpackage UserInterface
25 * @author Bharat Mediratta <bharat@menalto.com>
26 * @version $Revision: 17580 $
27 */
28class ItemEditPhotoThumbnail extends ItemEditPlugin {
29
30    /**
31     * @see ItemEditPlugin::handleRequest
32     */
33    function handleRequest($form, &$item, &$preferred) {
34	global $gallery;
35
36	$status = null;
37	$error = array();
38
39	if (isset($form['action']['crop']) || isset($form['action']['reset'])) {
40	    /* Load the thumbnail */
41	    list ($ret, $thumbnails) =
42		GalleryCoreApi::fetchThumbnailsByItemIds(array($item->getId()));
43	    if ($ret) {
44		return array($ret, null, null, null);
45	    }
46
47	    if (!empty($thumbnails)) {
48		$thumbnail = $thumbnails[$item->getId()];
49	    }
50	}
51
52	if (isset($form['action']['crop']) && isset($thumbnail)) {
53	    /* Get our source */
54	    list ($ret, $source) = GalleryCoreApi::loadEntitiesById(
55		$thumbnail->getDerivativeSourceId(),
56		array('GalleryFileSystemEntity', 'GalleryDerivative'));
57	    if ($ret) {
58		return array($ret, null, null, null);
59	    }
60
61	    list ($ret, $lock) = GalleryCoreApi::acquireWriteLock($thumbnail->getId());
62	    if ($ret) {
63		return array($ret, null, null, null);
64	    }
65
66	    list ($ret, $thumbnail) = $thumbnail->refresh();
67	    if ($ret) {
68		return array($ret, null, null, null);
69	    }
70
71	    if (empty($form['crop']['width'])) {
72		$cropX = 0;
73		$cropY = 0;
74		$cropWidth = 100;
75		$cropHeight = 100;
76	    } else {
77		/* Sanitize inputs to fit within 0-100 (if cropper miscalculates) */
78		$cropX = GalleryUtilities::roundToString(
79			max(0, 100 * $form['crop']['x'] / $source->getWidth()), 3);
80		$cropY = GalleryUtilities::roundToString(
81			max(0, 100 * $form['crop']['y'] / $source->getHeight()), 3);
82		$cropWidth = GalleryUtilities::roundToString(
83			min(100, 100 * $form['crop']['width'] / $source->getWidth()), 3);
84		$cropHeight = GalleryUtilities::roundToString(
85			min(100, 100 * $form['crop']['height'] / $source->getHeight()), 3);
86	    }
87
88	    list ($ret, $operations) =
89		GalleryCoreApi::mergeDerivativeOperations($thumbnail->getDerivativeOperations(),
90							  sprintf('crop|%s,%s,%s,%s',
91								  $cropX,
92								  $cropY,
93								  $cropWidth,
94								  $cropHeight),
95							  true);
96
97	    if ($ret) {
98		return array($ret, null, null, null);
99	    }
100	    $thumbnail->setDerivativeOperations($operations);
101
102	    $ret = $thumbnail->save();
103	    if ($ret) {
104		return array($ret, null, null, null);
105	    }
106
107	    $ret = GalleryCoreApi::releaseLocks($lock);
108	    if ($ret) {
109		return array($ret, null, null, null);
110	    }
111
112	    /* Figure out where to redirect upon success */
113	    list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'core');
114	    if ($ret) {
115		return array($ret, null, null, null);
116	    }
117	    $status = $module->translate('Thumbnail cropped successfully');
118	} else if (isset($form['action']['reset']) && isset($thumbnail)) {
119	    list ($ret, $lock) = GalleryCoreApi::acquireWriteLock($thumbnail->getId());
120	    if ($ret) {
121		return array($ret, null, null, null);
122	    }
123
124	    list ($ret, $thumbnail) = $thumbnail->refresh();
125	    if ($ret) {
126		return array($ret, null, null, null);
127	    }
128
129	    $operations = GalleryCoreApi::removeDerivativeOperation(
130					  'crop', $thumbnail->getDerivativeOperations());
131	    $thumbnail->setDerivativeOperations($operations);
132
133	    $ret = $thumbnail->save();
134	    if ($ret) {
135		return array($ret, null, null, null);
136	    }
137
138	    $ret = GalleryCoreApi::releaseLocks($lock);
139	    if ($ret) {
140		return array($ret, null, null, null);
141	    }
142
143	    /* Figure out where to redirect upon success */
144	    list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'core');
145	    if ($ret) {
146		return array($ret, null, null, null);
147	    }
148	    $status = $module->translate('Thumbnail reset successfully');
149	}
150
151	return array(null, $error, $status, false);
152    }
153
154    /**
155     * @see ItemEditPlugin::loadTemplate
156     */
157    function loadTemplate(&$template, &$form, $item, $thumbnail) {
158	global $gallery;
159
160	$ItemEditPhotoThumbnail = array();
161	$ItemEditPhotoThumbnail['showApplet'] = $thumbnail != null;
162
163	list ($ret, $ItemEditPhotoThumbnail['isAdmin']) = GalleryCoreApi::isUserInSiteAdminGroup();
164	if ($ret) {
165	    return array($ret, null, null);
166	}
167
168	if ($form['formName'] != 'ItemEditPhotoThumbnail') {
169	    /* First time around, reset the form */
170	    $form['formName'] = 'ItemEditPhotoThumbnail';
171	}
172
173	$targetThumbnailSize = 0;
174	$crop = array();
175	if (preg_match('/thumbnail\|(\d+)/',
176		       $thumbnail->getDerivativeOperations(),
177		       $matches)) {
178	    $targetThumbnailSize = $matches[1];
179	}
180
181	if (preg_match('/crop\|([\d\.]+),([\d\.]+),([\d\.]+),([\d\.]+)/',
182		       $thumbnail->getDerivativeOperations(),
183		       $matches)) {
184	    $crop['leftPercent'] = $matches[1];
185	    $crop['topPercent'] = $matches[2];
186	    $crop['widthPercent'] = $matches[3];
187	    $crop['heightPercent'] = $matches[4];
188	} else {
189	    /* No cropping yet */
190	    $crop['leftPercent'] = 0;
191	    $crop['topPercent'] = 0;
192	    $crop['widthPercent'] = 100;
193	    $crop['heightPercent'] = 100;
194	}
195
196	/*
197	 * The source may be quite large.  However, it's the only input image that we can really
198	 * display at this point because the resized derivatives though they may be smaller, can
199	 * have different derivative commands from the source.
200	 */
201	list ($ret, $source) = GalleryCoreApi::loadEntitiesById(
202	    $thumbnail->getDerivativeSourceId(),
203	    array('GalleryFileSystemEntity', 'GalleryDerivative'));
204	if ($ret) {
205	    return array($ret, null, null);
206	}
207
208	/* Convert crop percentages into real pixels */
209	$crop['left'] = round($crop['leftPercent'] * $source->getWidth() / 100);
210	$crop['top'] = round($crop['topPercent'] * $source->getHeight() / 100);
211	$crop['width'] = round($crop['widthPercent'] * $source->getWidth() / 100);
212	$crop['height'] = round($crop['heightPercent'] * $source->getHeight() / 100);
213
214	/*
215	 * It's possible that the source was created before we had an appropriate image toolkit,
216	 * so its dimensions can be set to zero, which will cause us problems.  In that case,
217	 * try rescanning it.
218	 */
219	$width = $source->getWidth();
220	if (empty($width)) {
221	    list ($ret, $lockId) = GalleryCoreApi::acquireWriteLock($source->getId());
222	    if ($ret) {
223		return array($ret, null, null);
224	    }
225	    list ($ret, $source) = $source->refresh();
226	    if ($ret) {
227		return array($ret, null, null);
228	    }
229	    $ret = $source->rescan();
230	    if ($ret) {
231		return array($ret, null, null);
232	    }
233	    $ret = $source->save();
234	    if ($ret) {
235		return array($ret, null, null);
236	    }
237	    $ret = GalleryCoreApi::releaseLocks($lockId);
238	    if ($ret) {
239		return array($ret, null, null);
240	    }
241	}
242
243	/* Make sure we have toolkit support */
244	list ($ret, $toolkit) =
245	    GalleryCoreApi::getToolkitByOperation($source->getMimeType(), 'crop');
246	if ($ret) {
247	    return array($ret, null, null);
248	}
249
250	$width = $source->getWidth();
251	if (empty($width) || !isset($toolkit)) {
252	    $ItemEditPhotoThumbnail['editThumbnail']['can']['crop'] = false;
253	} else {
254	    $ItemEditPhotoThumbnail['editThumbnail']['can']['crop'] = true;
255	    if (empty($crop)) {
256		$crop['left'] = 0;
257		$crop['top'] = 0;
258		$crop['width'] = $source->getWidth();
259		$crop['height'] = $source->getHeight();
260	    }
261
262	    /*
263	     * When we make the url for the image, force the session id into it.  Otherwise, the
264	     * IE JVM will send a request without the session cookie, then the server will make a
265	     * new session and send that cookie back to the JVM, which will overwrite the browser's
266	     * session with it, effectively logging out the user.
267	     */
268	    $urlGenerator =& $gallery->getUrlGenerator();
269	    $url = $urlGenerator->generateUrl(
270		array('view' => 'core.DownloadItem', 'itemId' => $source->getId()),
271		array('forceSessionId' => true, 'forceFullUrl' => true, 'htmlEntities' => false));
272
273	    $ItemEditPhotoThumbnail['editThumbnail']['appletCodeBase'] =
274		GalleryUtilities::convertPathToUrl(dirname(__FILE__)) . '/plugins';
275	    $ItemEditPhotoThumbnail['editThumbnail']['appletJarFile'] = 'ImageCrop.jar';
276	    $ItemEditPhotoThumbnail['editThumbnail']['imageUrl'] = $url;
277	    $ItemEditPhotoThumbnail['editThumbnail']['imageWidth'] = $source->getWidth();
278	    $ItemEditPhotoThumbnail['editThumbnail']['imageHeight'] = $source->getHeight();
279	    $ItemEditPhotoThumbnail['editThumbnail']['cropLeft'] = $crop['left'];
280	    $ItemEditPhotoThumbnail['editThumbnail']['cropTop'] = $crop['top'];
281	    $ItemEditPhotoThumbnail['editThumbnail']['cropWidth'] = $crop['width'];
282	    $ItemEditPhotoThumbnail['editThumbnail']['cropHeight'] = $crop['height'];
283	    $ItemEditPhotoThumbnail['editThumbnail']['targetThumbnailSize'] = $targetThumbnailSize;
284
285	    $aspectlist = array();
286	    list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'core');
287	    if ($ret) {
288		return array($ret, null, null);
289	    }
290
291	    $aspectRatioList[] = array('label' => $module->translate('Photo 5x3'),
292				       'width' => 5,
293				       'height' => 3);
294	    $aspectRatioList[] = array('label' => $module->translate('Photo 6x4'),
295				       'width' => 6,
296				       'height' => 4);
297	    $aspectRatioList[] = array('label' => $module->translate('Photo 7x5'),
298				       'width' => 7,
299				       'height' => 5);
300	    $aspectRatioList[] = array('label' => $module->translate('Photo 10x8'),
301				       'width' => 10,
302				       'height' => 8);
303	    $aspectRatioList[] = array('label' => $module->translate('Fullscreen 4x3'),
304				       'width' => 4,
305				       'height' => 3);
306	    $aspectRatioList[] = array('label' => $module->translate('Widescreen 16x9'),
307				       'width' => 16,
308				       'height' => 9);
309	    $aspectRatioList[] = array('label' => $module->translate('CinemaScope 2.35x1'),
310				       'width' => 47,
311				       'height' => 20);
312	    $aspectRatioList[] = array('label' => $module->translate('Square 1x1'),
313				       'width' => 1,
314				       'height' => 1);
315	    $aspectRatioList[] = array('label' => $module->translate('As Image'),
316				       'width' => $item->getWidth(),
317				       'height' => $item->getHeight());
318	    $ItemEditPhotoThumbnail['editThumbnail']['aspectRatioList'] = $aspectRatioList;
319
320	    $orientationList = array();
321	    $orientationList['landscape'] = $module->translate('Landscape');
322	    $orientationList['portrait'] = $module->translate('Portrait');
323	    $ItemEditPhotoThumbnail['editThumbnail']['orientationList'] = $orientationList;
324
325
326	    /*
327	     * Figure out which aspect ratio / orientation is closest to the current crop
328	     * settings so that we can start out with those values selected in the dropdowns.
329	     */
330	    $currentAspect = round($crop['width'] / $crop['height'], 2);
331	    $i = 0;
332
333	    /* Set defaults */
334	    $selectedAspect = 0;
335	    $selectedOrientation = 'portrait';
336	    $ItemEditPhotoThumbnail['editThumbnail']['cropRatioWidth'] =
337		$aspectRatioList[0]['width'];
338	    $ItemEditPhotoThumbnail['editThumbnail']['cropRatioHeight'] =
339		$aspectRatioList[0]['height'];
340
341	    foreach ($aspectRatioList as $aspect) {
342		$landscapeCompare = round($aspect['width'] / $aspect['height'], 2);
343		$portraitCompare = round($aspect['height'] / $aspect['width'], 2);
344		if (abs($currentAspect - $landscapeCompare) <= 0.03) {
345		    $selectedAspect = $i;
346		    $selectedOrientation = 'landscape';
347		    break;
348		} else if (abs($currentAspect - $portraitCompare) <= 0.03) {
349		    $selectedAspect = $i;
350		    $selectedOrientation = 'portrait';
351		    break;
352		}
353		$i++;
354	    }
355	    $ItemEditPhotoThumbnail['editThumbnail']['selectedAspect'] = $selectedAspect;
356	    $ItemEditPhotoThumbnail['editThumbnail']['selectedOrientation'] = $selectedOrientation;
357	    $ItemEditPhotoThumbnail['editThumbnail']['cropRatioWidth'] =
358		$aspectRatioList[$selectedAspect]['width'];
359	    $ItemEditPhotoThumbnail['editThumbnail']['cropRatioHeight'] =
360		$aspectRatioList[$selectedAspect]['height'];
361	}
362
363	$template->style('modules/core/templates/ItemEditPhotoThumbnail.css');
364	$template->setVariable('ItemEditPhotoThumbnail', $ItemEditPhotoThumbnail);
365	$template->setVariable('controller', 'core.ItemEditPhotoThumbnail');
366	return array(null,
367		     'modules/core/templates/ItemEditPhotoThumbnail.tpl', 'modules_core');
368    }
369
370    /**
371     * @see ItemEditPlugin::isSupported
372     */
373    function isSupported($item, $thumbnail) {
374	return ($thumbnail != null && GalleryUtilities::isA($item, 'GalleryPhotoItem'));
375    }
376
377    /**
378     * @see ItemEditPlugin::getTitle
379     */
380    function getTitle() {
381	list ($ret, $module) = GalleryCoreApi::loadPlugin('module', 'core');
382	if ($ret) {
383	    return array($ret, null);
384	}
385
386	return array(null, $module->translate('Crop Thumbnail'));
387    }
388}
389?>
390