001/*
002 $Id: Project.java 3406 2009-09-07 09:06:52Z gregory $
003
004 Copyright (C) 2006 Gregory Vincic, Olle Mansson
005 Copyright (C) 2007 Gregory Vincic
006
007 This file is part of Proteios.
008 Available at http://www.proteios.org/
009
010 Proteios is free software; you can redistribute it and/or modify it
011 under the terms of the GNU General Public License as published by
012 the Free Software Foundation; either version 2 of the License, or
013 (at your option) any later version.
014
015 Proteios is distributed in the hope that it will be useful, but
016 WITHOUT ANY WARRANTY; without even the implied warranty of
017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
018 General Public License for more details.
019
020 You should have received a copy of the GNU General Public License
021 along with this program; if not, write to the Free Software
022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
023 02111-1307, USA.
024 */
025package org.proteios.core;
026
027import org.proteios.core.data.ProjectData;
028import org.proteios.core.data.UserData;
029import org.proteios.core.query.EntityQuery;
030import org.proteios.core.query.Hql;
031import org.proteios.core.query.Restrictions;
032
033import java.util.Collections;
034import java.util.Map;
035import java.util.Set;
036
037/**
038 * This class is used to represent projects. A project is used to collect items
039 * of particular interest in one place. Users and groups can also be granted
040 * access to projects, allowing everyone easy access to shared items within the
041 * project.
042 * <p>
043 * While working with Proteios it is possible to set one project as the active
044 * project. This enables some useful functionality in the core:
045 * <ul>
046 * <li>Every {@link Shareable} item the user creates is automatically shared to
047 * the project.
048 * <li>All queries defaults to only return items that are shared to the
049 * project.
050 * </ul>
051 * <p>
052 * It is also possible to add an item to other projects, or remove it completely
053 * from all projects. Every member of a project has a special
054 * <code>project permission</code> which gives the highest permission for
055 * accessing items in the project. Normally this permission is
056 * {@link Permission#USE}. One person might be assigned administrator of the
057 * project and given higher permissions.
058 * <p>
059 * Items may also be given a permission within the project. The normal is to
060 * give full access, but it may also be set to for example
061 * {@link Permission#READ}. Then, not even the project administrator will get
062 * more than read permissions to the item. This feature can for example be used
063 * when a project needs to access data from a user that is not a member of the
064 * project.
065 * 
066 * @author Nicklas
067 * @version 2.0
068 * @see Role
069 * @see Project
070 * @see SessionControl#setActiveProject(Project)
071 * @base.modified $Date: 2009-09-07 11:06:52 +0200 (Mon, 07 Sep 2009) $
072 */
073public class Project
074                extends OwnedItem<ProjectData>
075                implements Nameable, Removable
076{
077        /**
078         * The type of item represented by this class.
079         * 
080         * @see Item#PROJECT
081         * @see #getType()
082         */
083        public static final Item TYPE = Item.PROJECT;
084        /**
085         * This filter will limit a query to only return projects where the logged
086         * in user is a member or owner unless the logged in user has generic read
087         * permission.
088         */
089        private static final QueryRuntimeFilter RUNTIME_FILTER = new QueryRuntimeFilterImpl();
090
091
092        /**
093         * Get a <code>Project</code> item when you know the ID.
094         * 
095         * @param dc The <code>DbControl</code> which will be used for permission
096         *        checking and database access.
097         * @param id The ID of the item to load
098         * @return The <code>Project</code> item
099         * @throws ItemNotFoundException If an item with the specified ID is not
100         *         found
101         * @throws PermissionDeniedException If the logged in user doesn't have
102         *         {@link Permission#READ} permission to the item
103         * @throws BaseException If there is another error
104         */
105        public static Project getById(DbControl dc, int id)
106                        throws ItemNotFoundException, PermissionDeniedException,
107                        BaseException
108        {
109                Project p = dc.loadItem(Project.class, id);
110                if (p == null)
111                        throw new ItemNotFoundException("Project[id=" + id + "]");
112                return p;
113        }
114
115
116        /**
117         * Get a query configured to retrieve projects. If the logged in user
118         * doesn't have generic permission to all projects, only projects the user
119         * has access to are returned.
120         * 
121         * @return An {@link ItemQuery} object
122         */
123        public static ItemQuery<Project> getQuery()
124        {
125                return new ItemQuery<Project>(Project.class, RUNTIME_FILTER);
126        }
127
128
129        Project(ProjectData projectData)
130        {
131                super(projectData);
132        }
133
134
135        /*
136         * From the Identifiable interface
137         * -------------------------------------------
138         */
139        public Item getType()
140        {
141                return TYPE;
142        }
143
144
145        public int getProjectType()
146 {
147                                        return getData().getProjectType();
148 }
149
150 public void setProjectType(int projectType)
151 {
152   checkPermission(Permission.WRITE);
153   getData().setProjectType(projectType);
154 }
155
156        // -------------------------------------------
157        /*
158         * From the Nameable interface -------------------------------------------
159         */
160        public String getName()
161        {
162                return getData().getName();
163        }
164
165
166        public void setName(String name)
167                        throws PermissionDeniedException, InvalidDataException
168        {
169                checkPermission(Permission.WRITE);
170                NameableUtil.setName(getData(), name);
171        }
172
173
174        public String getDescription()
175        {
176                return getData().getDescription();
177        }
178
179
180        public void setDescription(String description)
181                        throws PermissionDeniedException, InvalidDataException
182        {
183                checkPermission(Permission.WRITE);
184                NameableUtil.setDescription(getData(), description);
185        }
186
187
188        // -------------------------------------------
189        /*
190         * From the Removable interface -------------------------------------------
191         */
192        public boolean isRemoved()
193        {
194                return getData().isRemoved();
195        }
196
197
198        public void setRemoved(boolean removed)
199                        throws PermissionDeniedException
200        {
201                checkPermission(removed ? Permission.DELETE : Permission.WRITE);
202                getData().setRemoved(removed);
203        }
204
205
206        // -------------------------------------------
207        /*
208         * From the BasicItem class -------------------------------------------
209         */
210        /**
211         * Always return FALSE. A project can be referenced from users, groups and
212         * project keys, but those references are automatically deleted if the
213         * project is deleted and aren't inclued in this check.
214         */
215        @Override
216        public boolean isUsed()
217                        throws BaseException
218        {
219                return false;
220        }
221
222
223        /**
224         * If the logged in user is a member of this project, read or use permission
225         * is granted.
226         */
227        @Override
228        void initPermissions(int granted, int denied)
229                        throws BaseException
230        {
231                int projectPermission = getSessionControl().getProjectPermission(this);
232                granted |= projectPermission & Permission.grant(Permission.READ,
233                        Permission.USE);
234                super.initPermissions(granted, denied);
235        }
236
237
238        // -------------------------------------------
239        /**
240         * Grant a user permissions to this project. Use an empty <code>Set</code>
241         * or null to remove all permissions for the user.
242         * 
243         * @param user The <code>User</code>
244         * @param permissions The permissions to grant, or null to revoke all
245         *        permissions
246         * @throws PermissionDeniedException If the logged in user doesn't have
247         *         {@link Permission#WRITE} permission for the project
248         * @throws InvalidDataException If the user is null
249         * @see Permission
250         */
251        public void setPermissions(User user, Set<Permission> permissions)
252                        throws PermissionDeniedException, InvalidDataException
253        {
254                checkPermission(Permission.WRITE);
255                if (user == null)
256                        throw new InvalidUseOfNullException("user");
257                if (permissions == null || permissions.isEmpty())
258                {
259                        getData().getUsers().remove(user.getData());
260                }
261                else
262                {
263                        getData().getUsers().put(user.getData(),
264                                Permission.grant(permissions));
265                }
266        }
267
268
269        /**
270         * Get the permissions for a user in this project.
271         * 
272         * @param user The <code>User</code> for which we want to get the
273         *        permission
274         * @return A <code>Set</code> containing the granted permissions, or an
275         *         empty set if no permissions have been granted
276         * @throws InvalidDataException If the user is null
277         * @see Permission
278         */
279        public Set<Permission> getPermissions(User user)
280                        throws InvalidDataException
281        {
282                if (user == null)
283                        throw new InvalidUseOfNullException("user");
284                Map<UserData, Integer> users = getData().getUsers();
285                UserData data = user.getData();
286                Integer i = users.get(data);
287                if(i ==null)
288                        i = 0;          
289                return Permission.fromInt(i);
290        }
291
292
293        /**
294         * Get a query that returns the users that are members of this project. This
295         * query excludes users that the logged in user doesn't have permission to
296         * read.
297         * 
298         * @see User#getQuery()
299         */
300        public ItemQuery<User> getUsers()
301                        throws BaseException
302        {
303                ItemQuery<User> query = User.getQuery();
304                query.joinPermanent(Hql.innerJoin("projects", Item.PROJECT.getAlias()));
305                query.restrictPermanent(Restrictions.eq(Hql.alias(Item.PROJECT
306                        .getAlias()), Hql.entity(this)));
307                return query;
308        }
309
310
311        /**
312         * Grant a group permissions to this project. Use an empty <code>Set</code>
313         * or null to remove all permissions for the group. To share to the
314         * {@link Group#EVERYONE} group {@link Permission#SHARE_TO_EVERYONE} is
315         * required.
316         * 
317         * @param group The <code>Group</code>
318         * @param permissions The permissions to grant, or null to revoke all
319         *        permissions
320         * @throws PermissionDeniedException If the logged in user doesn't have
321         *         {@link Permission#WRITE} permission for the project
322         * @throws InvalidDataException If the group is null
323         * @throws BaseException If there is another error
324         * @see Permission
325         */
326        public void setPermissions(Group group, Set<Permission> permissions)
327                        throws PermissionDeniedException, InvalidDataException,
328                        BaseException
329        {
330                checkPermission(Permission.WRITE);
331                if (group == null)
332                        throw new InvalidUseOfNullException("group");
333                if ((group.getId() == SystemItems.getId(Group.EVERYONE)) && !getSessionControl()
334                        .hasSystemPermission(Permission.SHARE_TO_EVERYONE))
335                {
336                        throw new PermissionDeniedException(
337                                "Not allowed to share to the EVERYONE group.");
338                }
339                if (permissions == null || permissions.isEmpty())
340                {
341                        getData().getGroups().remove(group.getData());
342                }
343                else
344                {
345                        getData().getGroups().put(group.getData(),
346                                Permission.grant(permissions));
347                }
348        }
349
350
351        /**
352         * Get the permissions for a group in this project.
353         * 
354         * @param group The <code>Group</code> for which we want to get the
355         *        permission
356         * @return An <code>EnumSet</code> containing the granted permissions, or
357         *         an empty set if no permissions have been granted
358         * @throws InvalidDataException If the group is null
359         * @see Permission
360         */
361        public Set<Permission> getPermissions(Group group)
362                        throws InvalidDataException
363        {
364                if (group == null)
365                        throw new InvalidUseOfNullException("usgroup");
366                return Permission.fromInt(getData().getGroups().get(group.getData()));
367        }
368
369
370        /**
371         * Get a query that returns the groups that are members of this project.
372         * This query excludes groups that the logged in user doesn't have
373         * permission to read.
374         * 
375         * @see Group#getQuery()
376         */
377        public ItemQuery<Group> getGroups()
378        {
379                ItemQuery<Group> query = Group.getQuery();
380                query.joinPermanent(Hql.innerJoin("projects", Item.PROJECT.getAlias()));
381                query.restrictPermanent(Restrictions.eq(Hql.alias(Item.PROJECT
382                        .getAlias()), Hql.entity(this)));
383                return query;
384        }
385
386        /**
387         * A runtime filter implementation that limits a query to only return
388         * projects where the logged in user is a member or owner unless the logged
389         * in user has generic read permission.
390         */
391        private static class QueryRuntimeFilterImpl
392                        implements QueryRuntimeFilter
393        {
394                public void enableFilters(QueryRuntimeFilterManager manager,
395                                EntityQuery query, DbControl dc)
396                {
397                        org.hibernate.Filter filter = null;
398                        SessionControl sc = dc.getSessionControl();
399                        int owner = sc.getLoggedInUserId();
400                        Set<Integer> projects = sc.getProjects();
401                        if (projects == null || projects.size() == 0)
402                                projects = Collections.singleton(0);
403                        if (query.isIncluded(Include.OTHERS) && sc.hasPermission(
404                                Permission.READ, query.getItemType()))
405                        {
406                                // Logged in user has generic read permission and wants other
407                                // user's items...
408                                if (!query.isIncluded(Include.MINE))
409                                {
410                                        // ... but not the one's with ownership
411                                        filter = manager.enableFilter("notOwnedBy");
412                                        if (filter != null)
413                                        {
414                                                filter.setParameter("owner", owner);
415                                        }
416                                }
417                        }
418                        else if (query.isIncluded(Include.MINE, Include.SHARED))
419                        {
420                                // Logged in user wants own and shared projects
421                                filter = manager.enableFilter("ownedByOrMemberOf");
422                                if (filter != null)
423                                {
424                                        filter.setParameter("owner", owner);
425                                        filter.setParameterList("items", projects);
426                                }
427                        }
428                        else if (query.isIncluded(Include.SHARED))
429                        {
430                                // Logged in user wants only shared projects
431                                filter = manager.enableFilter("memberOf");
432                                if (filter != null)
433                                {
434                                        filter.setParameter("owner", owner);
435                                        filter.setParameterList("items", projects);
436                                }
437                        }
438                        else if (query.isIncluded(Include.MINE))
439                        {
440                                // Logged in user only wants own projects
441                                filter = manager.enableFilter("ownedBy");
442                                if (filter != null)
443                                {
444                                        filter.setParameter("owner", owner);
445                                }
446                        }
447                        else
448                        {
449                                manager.enableFilter("denyAll");
450                        }
451                }
452        }
453
454
455        /**
456         * Fetches the project directory id.
457         * This method does not need to create any directory
458         * item, as the id value is stored in the database.
459         * 
460         * @return int The project directory id.
461         */
462        public int fetchProjectDirectoryId()
463        {
464                return getData().getProjectDirectory().getId();
465        }
466
467
468        public Directory getProjectDirectory()
469        {
470                ItemFactory factory = new ItemFactory(getDbControl());
471                return factory
472                        .getItem(Directory.class, getData().getProjectDirectory());
473        }
474
475
476        public void setProjectDirectory(Directory projectDirectory)
477        {
478                if (projectDirectory == null)
479                        throw new InvalidUseOfNullException("");
480                getData().setProjectDirectory(projectDirectory.getData());
481        }
482
483 public boolean isClosed()
484 {
485                checkPermission(Permission.READ);
486                return getData().isClosed();
487 }
488
489 public void setClosed(boolean closed)
490 {
491                checkPermission(Permission.WRITE);
492                getData().setClosed(closed);
493 }
494}