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 controller will handle the changes in the permission of an item
23 * @package GalleryCore
24 * @subpackage UserInterface
25 * @author Bharat Mediratta <bharat@menalto.com>
26 * @version $Revision: 17580 $
27 */
28class ItemPermissionsController extends GalleryController {
29
30    /**
31     * @see GalleryController::handleRequest
32     */
33    function handleRequest($form) {
34	global $gallery;
35
36	list ($ret, $item) = $this->getItem();
37	if ($ret) {
38	    return array($ret, null);
39	}
40	$itemId = $item->getId();
41
42	/* Make sure we have permission to change permissions of this item */
43	$ret = GalleryCoreApi::assertHasItemPermission($itemId, 'core.changePermissions');
44	if ($ret) {
45	    return array($ret, null);
46	}
47
48	$applyToChildren = isset($form['applyToSubItems']);
49	$status = $error = array();
50	if (isset($form['action']['deleteGroupPermission'])) {
51	    /* Figure out which one we're working with */
52	    $deleteGroupPermission = array_keys($form['action']['deleteGroupPermission']);
53	    $index = array_pop($deleteGroupPermission);
54
55	    /* Handle delete group perm actions */
56	    list ($groupId, $permissionId) = explode(',', $form['group']['delete'][$index]);
57	    $ret = GalleryCoreApi::removeGroupPermission($itemId, $groupId,
58							 $permissionId, $applyToChildren);
59	    if ($ret) {
60		return array($ret, null);
61	    }
62
63	    /* Figure out where to redirect upon success */
64	    $redirect['view'] = 'core.ItemAdmin';
65	    $redirect['subView'] = 'core.ItemPermissions';
66	    $redirect['itemId'] = $itemId;
67	    $status['deletedGroupPermission'] = 1;
68
69	    /* Stuff the values back into the form for easy re-adding */
70	    $redirect['form[group][permission]'] = $permissionId;
71	    list ($ret, $group) = GalleryCoreApi::loadEntitiesById($groupId, 'GalleryGroup');
72	    if (!$ret) {
73		$redirect['form[group][groupName]'] = $group->getGroupName();
74	    }
75	    $verifySelfPermissions = true;
76	} else if (isset($form['action']['deleteUserPermission'])) {
77	    /* Figure out which one we're working with */
78	    $deleteUserPermission = array_keys($form['action']['deleteUserPermission']);
79	    $index = array_pop($deleteUserPermission);
80
81	    /* Handle delete user perm actions */
82	    list ($userId, $permissionId) = explode(',', $form['user']['delete'][$index]);
83	    $ret = GalleryCoreApi::removeUserPermission($itemId, $userId,
84							$permissionId, $applyToChildren);
85	    if ($ret) {
86		return array($ret, null);
87	    }
88
89	    /* Figure out where to redirect upon success */
90	    $redirect['view'] = 'core.ItemAdmin';
91	    $redirect['subView'] = 'core.ItemPermissions';
92	    $redirect['itemId'] = $itemId;
93	    $status['deletedUserPermission'] = 1;
94
95	    /* Stuff the values back into the form for easy re-adding */
96	    $redirect['form[user][permission]'] = $permissionId;
97	    list ($ret, $user) = GalleryCoreApi::loadEntitiesById($userId, 'GalleryUser');
98	    if (!$ret) {
99		$redirect['form[user][userName]'] = $user->getUserName();
100	    }
101	    if ($userId == $gallery->getActiveUserId()) {
102		$verifySelfPermissions = true;
103	    }
104	} else if (isset($form['action']['addUserPermission'])) {
105
106	    /* Handle add user permission actions */
107	    if (empty($form['user']['userName'])) {
108		$error[] = 'form[error][user][missingUser]';
109	    } else {
110		/* Validate the user */
111		list ($ret, $user) = GalleryCoreApi::fetchUserByUserName($form['user']['userName']);
112		if ($ret) {
113		    if ($ret->getErrorCode() & ERROR_MISSING_OBJECT) {
114			$error[] = 'form[error][user][invalidUser]';
115		    } else {
116			return array($ret, null);
117		    }
118		}
119	    }
120
121	    /* Validate the permission */
122	    $permission = $form['user']['permission'];
123	    list ($ret, $allPermissions) = GalleryCoreApi::getPermissionIds();
124	    if ($ret) {
125		return array($ret, null);
126	    }
127	    if (empty($allPermissions[$permission])) {
128		$error[] = 'form[error][user][invalidPermission]';
129	    }
130
131	    if (empty($error)) {
132		/* Don't add the permission if it already exists */
133		list ($ret, $hasIt) =
134		    GalleryCoreApi::hasPermission($itemId, $user->getId(), $permission);
135		if ($ret) {
136		    return array($ret, null);
137		}
138		if ($hasIt) {
139		    $error[] = 'form[error][user][alreadyHadPermission]';
140		}
141	    }
142
143	    if (empty($error)) {
144		$ret = GalleryCoreApi::addUserPermission($itemId, $user->getId(),
145							 $permission, $applyToChildren);
146		if ($ret) {
147		    return array($ret, null);
148		}
149
150		/* Figure out where to redirect upon success */
151		$redirect['view'] = 'core.ItemAdmin';
152		$redirect['subView'] = 'core.ItemPermissions';
153		$redirect['itemId'] = $itemId;
154		$redirect['form[user][userName]'] = $user->getUserName();
155		$status['addedUserPermission'] = 1;
156	    }
157	} else if (isset($form['action']['addGroupPermission'])) {
158
159	    /* Handle add group permission actions */
160	    if (empty($form['group']['groupName'])) {
161		$error[] = 'form[error][group][missingGroup]';
162	    } else {
163		/* Validate the group */
164		list ($ret, $group) =
165		    GalleryCoreApi::fetchGroupByGroupName($form['group']['groupName']);
166		if ($ret) {
167		    if ($ret->getErrorCode() & ERROR_MISSING_OBJECT) {
168			$error[] = 'form[error][group][invalidGroup]';
169		    } else {
170			return array($ret, null);
171		    }
172		}
173	    }
174
175	    /* Validate the permission */
176	    $permission = $form['group']['permission'];
177	    list ($ret, $allPermissions) = GalleryCoreApi::getPermissionIds();
178	    if ($ret) {
179		return array($ret, null);
180	    }
181	    if (empty($allPermissions[$permission])) {
182		$error[] = 'form[error][group][invalidPermission]';
183	    }
184
185	    if (empty($error)) {
186		/* Don't add the permission if it already exists */
187		list ($ret, $hasIt) =
188		    GalleryCoreApi::hasPermission($itemId, $group->getId(), $permission);
189		if ($ret) {
190		    return array($ret, null);
191		}
192		if ($hasIt) {
193		    $error[] = 'form[error][group][alreadyHadPermission]';
194		}
195	    }
196
197	    if (empty($error)) {
198		$ret = GalleryCoreApi::addGroupPermission($itemId, $group->getId(),
199							  $permission, $applyToChildren);
200		if ($ret) {
201		    return array($ret, null);
202		}
203
204		/* Figure out where to redirect upon success */
205		$redirect['view'] = 'core.ItemAdmin';
206		$redirect['subView'] = 'core.ItemPermissions';
207		$redirect['itemId'] = $itemId;
208		$redirect['form[group][groupName]'] = $group->getGroupName();
209		$status['addedGroupPermission'] = 1;
210	    }
211	} else if (isset($form['action']['changeOwner'])) {
212	    if (empty($form['owner']['ownerName'])) {
213		$error[] = 'form[error][owner][missingUser]';
214	    } else {
215		$ret = GalleryCoreApi::assertUserIsSiteAdministrator();
216		if ($ret) {
217		    return array($ret, null);
218		}
219
220		/* Validate the user */
221		list ($ret, $user) =
222		    GalleryCoreApi::fetchUserByUserName($form['owner']['ownerName']);
223		if ($ret) {
224		    if ($ret->getErrorCode() & ERROR_MISSING_OBJECT) {
225			$error[] = 'form[error][owner][invalidUser]';
226		    } else {
227			return array($ret, null);
228		    }
229		}
230	    }
231
232	    if (empty($error)) {
233		list ($ret, $permissions) =
234		    GalleryCoreApi::fetchPermissionsForItems(array($itemId), $item->getOwnerId());
235		if ($ret) {
236		    return array($ret, null);
237		}
238
239		list ($ret, $lockId) = GalleryCoreApi::acquireWriteLock($itemId);
240		if ($ret) {
241		    return array($ret, null);
242		}
243
244		list ($ret, $item) = $item->refresh();
245		if ($ret) {
246		    return array($ret, null);
247		}
248
249		$userId = $user->getId();
250		$item->setOwnerId($userId);
251		$item->setSerialNumber($form['serialNumber']);
252		$ret = $item->save();
253		if ($ret) {
254		    GalleryCoreApi::releaseLocks($lockId);
255		    return array($ret, null);
256		}
257
258		$ret = GalleryCoreApi::releaseLocks($lockId);
259		if ($ret) {
260		    return array($ret, null);
261		}
262
263		$applyOwnerToChildren = isset($form['applyOwnerToSubItems']);
264		foreach ($permissions[$itemId] as $permission => $unused) {
265		    $ret = GalleryCoreApi::addUserPermission($itemId, $userId,
266							     $permission, $applyOwnerToChildren);
267		    if ($ret) {
268			return array($ret, null);
269		    }
270		}
271
272		/* change the owner recursively for the descendents */
273		if ($applyOwnerToChildren) {
274		    list ($ret, $descendentIds) =
275			GalleryCoreApi::fetchDescendentItemIds($item, null, null, 'core.all');
276
277		    /*
278		     * Process these descendents in chunks since we may have thousands of
279		     * items and we don't want to give the database a heart attack.
280		     */
281		    $chunkSize = 200;
282		    while (!empty($descendentIds)) {
283			$chunk = array_splice($descendentIds, 0, $chunkSize);
284			$gallery->guaranteeTimeLimit(60);
285
286			list ($ret, $lockId) = GalleryCoreApi::acquireWriteLock($chunk);
287			if ($ret) {
288			    return array($ret, null);
289			}
290
291			list ($ret, $descendents) =
292			    GalleryCoreApi::loadEntitiesById($chunk, 'GalleryItem');
293			if ($ret) {
294			    return array($ret, null);
295			}
296			foreach ($descendents as $item) {
297			    $item->setOwnerId($userId);
298			    $ret = $item->save();
299			    if ($ret) {
300				GalleryCoreApi::releaseLocks($lockId);
301				return array($ret, null);
302			    }
303			}
304			$ret = GalleryCoreApi::releaseLocks($lockId);
305			if ($ret) {
306			    return array($ret, null);
307			}
308		    }
309		}
310
311		/* Figure out where to redirect upon success */
312		$redirect['view'] = 'core.ItemAdmin';
313		$redirect['subView'] = 'core.ItemPermissions';
314		$redirect['itemId'] = $itemId;
315		$status['changedOwner'] = 1;
316	    }
317	}
318
319	if (isset($verifySelfPermissions)) {
320	    /*
321	     * Make sure we don't remove our own ability to change permissions on this item.
322	     * If this was a recursive remove we may lose permissions on subitems.
323	     */
324	    list ($ret, $canEdit) = GalleryCoreApi::hasItemPermission($itemId, 'core.edit');
325	    if ($ret) {
326		return array($ret, null);
327	    }
328	    if (!$canEdit) {
329		$ret = GalleryCoreApi::addUserPermission($itemId, $gallery->getActiveUserId(),
330							 'core.edit', false);
331		if ($ret) {
332		    return array($ret, null);
333		}
334		$status['addedBackSelfPermission'] = 1;
335	    }
336	    list ($ret, $canChange) =
337		GalleryCoreApi::hasItemPermission($itemId, 'core.changePermissions');
338	    if ($ret) {
339		return array($ret, null);
340	    }
341	    if (!$canChange) {
342		$ret = GalleryCoreApi::addUserPermission($itemId, $gallery->getActiveUserId(),
343							 'core.changePermissions', false);
344		if ($ret) {
345		    return array($ret, null);
346		}
347		$status['addedBackSelfPermission'] = 1;
348	    }
349	}
350
351	if (empty($error)) {
352	    /*
353	     * Try compacting.  Ignore lock timeouts here; if we failed this time we'll try
354	     * again next time.
355	     */
356	    $ret = GalleryCoreApi::maybeCompactAccessLists();
357	    if ($ret && !($ret->getErrorCode() & ERROR_LOCK_TIMEOUT)) {
358		return array($ret, null);
359	    }
360	}
361
362	if (!empty($redirect)) {
363	    $results['redirect'] = $redirect;
364	} else {
365	    $results['delegate']['view'] = 'core.ItemAdmin';
366	    $results['delegate']['subView'] = 'core.ItemPermissions';
367	}
368	$results['status'] = $status;
369	$results['error'] = $error;
370
371	return array(null, $results);
372    }
373}
374
375/**
376 * This view will prompt for permission settings of an item
377 */
378class ItemPermissionsView extends GalleryView {
379
380    /**
381     * @see GalleryView::loadTemplate
382     */
383    function loadTemplate(&$template, &$form) {
384	global $gallery;
385
386	list ($ret, $item) = $this->getItem();
387	if ($ret) {
388	    return array($ret, null);
389	}
390	$itemId = $item->getId();
391
392	/* Make sure we have permission to edit this item */
393	$ret = GalleryCoreApi::assertHasItemPermission($itemId, 'core.edit');
394	if ($ret) {
395	    return array($ret, null);
396	}
397	list ($ret, $canChange) =
398	    GalleryCoreApi::hasItemPermission($itemId, 'core.changePermissions');
399	if ($ret) {
400	    return array($ret, null);
401	}
402
403	$form['serialNumber'] = $item->getSerialNumber();
404
405	if ($form['formName'] == 'ItemPermissions') {
406	    /* Complain if we have any invalid data */
407	} else {
408	    /*
409	     * First time around, load the form with item data.  Note that
410	     * userName and groupName can be passed in to this form so don't
411	     * initialize them unless they don't exist.
412	     */
413	    if (empty($form['user']['userName'])) {
414		$form['user']['userName'] = '';
415	    }
416
417	    if (empty($form['user']['permission'])) {
418		$form['user']['permission'] = '';
419	    }
420
421	    if (empty($form['group']['groupName'])) {
422		$form['group']['groupName'] = '';
423	    }
424
425	    if (empty($form['group']['permission'])) {
426		$form['group']['permission'] = '';
427	    }
428
429	    $form['owner']['ownerName'] = '';
430	    $form['formName'] = 'ItemPermissions';
431	}
432
433	/* Get all available permissions */
434	list ($ret, $allPermissions) = GalleryCoreApi::getPermissionIds();
435	if ($ret) {
436	    return array($ret, null);
437	}
438	ksort($allPermissions);
439
440	/* Get all permissions for the item. */
441	list ($ret, $permissions) =
442	    GalleryCoreApi::fetchAllPermissionsForItem($itemId, true);
443	if ($ret) {
444	    return array($ret, null);
445	}
446
447	/* Figure out all the unique user/group ids and load those */
448	$userAndGroupEntityIds = array();
449	foreach ($permissions as $permission) {
450	    if (!empty($permission['userId'])) {
451		$userAndGroupEntityIds[$permission['userId']] = 1;
452	    }
453	    if (!empty($permission['groupId'])) {
454		$userAndGroupEntityIds[$permission['groupId']] = 1;
455	    }
456	}
457
458	list ($ret, $userAndGroupEntities) = GalleryCoreApi::loadEntitiesById(
459		array_keys($userAndGroupEntityIds), array('GalleryUser', 'GalleryGroup'));
460	if ($ret) {
461	    return array($ret, null);
462	}
463
464	/* Convert them into a hash map by entity id */
465	foreach ($userAndGroupEntities as $entity) {
466	    $userAndGroupEntityMap[$entity->getId()] = (array)$entity;
467	}
468
469	/* Figure out the admin group id */
470	list ($ret, $adminGroupId) =
471	    GalleryCoreApi::getPluginParameter('module', 'core', 'id.adminGroup');
472	if ($ret) {
473	    return array($ret, null);
474	}
475
476	/*
477	 * Now create the separate user and group permission maps.
478	 *
479	 * Silently ignore any permissions that we come across that aren't part
480	 * of the permission registry.  They may be permission associated with
481	 * modules that are not currently active.
482	 */
483	$userPermissions = $groupPermissions = array();
484	foreach ($permissions as $permission) {
485	    $permissionId = $permission['permission'];
486	    if (!empty($permission['userId']) && isset($allPermissions[$permissionId])) {
487		list ($ret, $subPermissions) = GalleryCoreApi::getSubPermissions($permissionId);
488		if ($ret) {
489		    return array($ret, null);
490		}
491
492		$userPermissions[] = array(
493		    'permission' => array('id' => $permissionId,
494					  'description' => $allPermissions[$permissionId]),
495		    'user' => $userAndGroupEntityMap[$permission['userId']],
496		    'deleteList' => $subPermissions);
497	    }
498
499	    if (!empty($permission['groupId']) && isset($allPermissions[$permissionId])) {
500		if ($permission['groupId'] != $adminGroupId) {
501		    list ($ret, $subPermissions) = GalleryCoreApi::getSubPermissions($permissionId);
502		    if ($ret) {
503			return array($ret, null);
504		    }
505		} else {
506		    $subPermissions = array();
507		}
508
509		$groupPermissions[] =
510		    array('permission' => array('id' => $permissionId,
511						'description' => $allPermissions[$permissionId]),
512			  'group' => $userAndGroupEntityMap[$permission['groupId']],
513			  'deleteList' => $subPermissions);
514	    }
515	}
516
517	/* Figure out the owner */
518	list ($ret, $owner) = GalleryCoreApi::loadEntitiesById($item->getOwnerId(), 'GalleryUser');
519	if ($ret) {
520	    return array($ret, null);
521	}
522
523	list ($ret, $isAdmin) = GalleryCoreApi::isUserInSiteAdminGroup();
524	if ($ret) {
525	    return array($ret, null);
526	}
527
528	/* Figure out what we can display on the form */
529	$can['changePermissions'] = $canChange;
530	$can['changeOwner'] = $isAdmin;
531	$can['applyToSubItems'] = $item->getCanContainChildren();
532
533	$ItemPermissions['owner'] = (array)$owner;
534	$ItemPermissions['can'] = $can;
535	$ItemPermissions['userPermissions'] = $userPermissions;
536	$ItemPermissions['groupPermissions'] = $groupPermissions;
537	$ItemPermissions['allPermissions'] = $allPermissions;
538
539	$template->setVariable('ItemPermissions', $ItemPermissions);
540	$template->setVariable('controller', 'core.ItemPermissions');
541	return array(null, array('body' => 'modules/core/templates/ItemPermissions.tpl'));
542    }
543
544    /**
545     * @see GalleryView::getViewDescription
546     */
547    function getViewDescription() {
548	list ($ret, $core) = GalleryCoreApi::loadPlugin('module', 'core');
549	if ($ret) {
550	    return array($ret, null);
551	}
552
553	list ($ret, $item) = $this->getItem();
554	if ($ret) {
555	    return array($ret, null);
556	}
557
558	$itemTypeNames = $item->itemTypeName(true);
559
560	return array(null, $core->translate(array('text' => 'edit %s permissions',
561						  'arg1' => $itemTypeNames[1])));
562    }
563}
564?>
565