[opw-web] Centralize permissions / Implement late_submissions / Restrict changing organizations



commit d7936ded7fe31ebb3aa72fb95d422e3a20685b0e
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Tue Mar 18 14:16:36 2014 -0400

    Centralize permissions / Implement late_submissions / Restrict changing organizations
    
    * Add a central get_project_permissions() call to get the permissions
      the user has related to submitting and editing projects.
    
    * Restrict the ability to submit projects between dl_student and dl_mentor
      based on organization.late_submission.
    
    * Allow editing of projects between dl_student and dl_mentor.
    
    * Restrict the ability to change organizations - once a organization
      is no longer taking submissions, don't allow projects to be moved
      to or from that organization.

 lang/en-gb.php                                     |    1 +
 modules/mod_manage_projects.php                    |    4 +-
 modules/mod_program_home.php                       |   10 +-
 modules/mod_view_projects.php                      |  163 +++++++-------------
 skins/easterngreen/html/tpl_program_home.html      |    3 +
 .../html/tpl_view_projects_editor.html             |    4 +
 utils.php                                          |  109 +++++++++++++-
 7 files changed, 180 insertions(+), 114 deletions(-)
---
diff --git a/lang/en-gb.php b/lang/en-gb.php
index 5e8dfa3..0363628 100644
--- a/lang/en-gb.php
+++ b/lang/en-gb.php
@@ -130,6 +130,7 @@ $lang_data = array(
     'view_submissions'      => 'View my submissions',
     'create_project'        => 'Create new project',
     'mentor_project_sel'    => 'Select project to mentor',
+    'late_submission_notice' => 'The general application deadline has passed, however some organizations are 
still accepting applications.',
     'cancel_mentor'         => 'Cancel mentor application',
     'view_proposals'        => 'View project proposals',
     'resign_program'        => 'Resign from this program',
diff --git a/modules/mod_manage_projects.php b/modules/mod_manage_projects.php
index caddf28..501f860 100644
--- a/modules/mod_manage_projects.php
+++ b/modules/mod_manage_projects.php
@@ -50,8 +50,8 @@ $can_edit = $user->is_admin || ($organization_id == $user_organization_id);
 // Only admins and mentors can access
 $user->restrict($role == 'm', true);
 
-$organization_select = build_organization_select($program_id, $organization_id, false,
-                                                 null, "organizationSelect");
+$organization_select = build_organization_select($program_id, $organization_id, false, false,
+                                                 false, null, "organizationSelect");
 
 if ($organization_id > 0) {
     $sql = "SELECT * FROM {$db->prefix}projects prj " .
diff --git a/modules/mod_program_home.php b/modules/mod_program_home.php
index 8f0d93d..dc82adf 100644
--- a/modules/mod_program_home.php
+++ b/modules/mod_program_home.php
@@ -18,13 +18,12 @@ if ($program_data != null)
     $user->get_role($id, $role, $organization);
 
     // Set object availability based on deadlines
-    $show_student = true;
     $show_mentor = true;
 
-    if ($core->timestamp >= $program_data['dl_student'])
-    {
-        $show_student = false;
-    }
+    $project_permissions = get_project_permissions($program_data, $role, null);
+
+    $show_student = $project_permissions->can_submit;
+    $late_submission = $project_permissions->late_submission;
 
     if ($core->timestamp >= $program_data['dl_mentor'])
     {
@@ -54,6 +53,7 @@ if ($program_data != null)
         'prg_interm_visibility'    => $skin->visibility($role == 'i'),
         'prg_mentor_visibility'    => $skin->visibility($role == 'm'),
         'dl_student_visibility'    => $skin->visibility($show_student),
+        'late_submission_visibility' => $skin->visibility($late_submission),
         'dl_mentor_visibility'     => $skin->visibility($show_mentor),
         'started_visibility'       => $skin->visibility($role == 'g' && !($show_student || $show_mentor)),
         'prog_adm_visibility'      => $skin->visibility($user->is_admin),
diff --git a/modules/mod_view_projects.php b/modules/mod_view_projects.php
index 1a43c17..56d7a1e 100644
--- a/modules/mod_view_projects.php
+++ b/modules/mod_view_projects.php
@@ -28,32 +28,23 @@ $mentor_remove = isset($_POST['mentor_remove']);
 $project_save = isset($_POST['project_save']);
 $confirm = isset($_POST['yes']);
 
-// Validate project and program ID
+// Get information about the user and program, and project
+$user->get_role($program_id, $role, $mentor_organization_id);
+$program_data = $cache->get_program_data($program_id);
+$user->restrict($program_data !== null);
+
 if ($project_id > 0)
 {
-    $sql = "SELECT COUNT(*) AS count " .
-           "FROM {$db->prefix}projects prj " .
-           "LEFT JOIN {$db->prefix}programs prg " .
-           "ON prg.id = prj.program_id " .
-           "WHERE prj.id = :project_id " .
-           "AND prg.id = :program_id " .
-           (!$user->is_admin ? "AND prg.is_active = 1" : "");
+    $project_data = $cache->get_project_data($project_id);
+
+    $user->restrict($project_data !== null);
+    $user->restrict($project_data['program_id'] == $program_id);
 }
 else
 {
-    $sql = "SELECT COUNT(*) AS count " .
-           "FROM {$db->prefix}programs " .
-           "WHERE id = :program_id " .
-           (!$user->is_admin ? "AND is_active = 1" : "");
+    $project_data = null;
 }
 
-$row = $db->query($sql,
-                  array('program_id' => $program_id,
-                        'project_id' => $project_id),
-                  true);
-
-$user->restrict($row['count'] > 0);
-
 // Validate organization_id
 if ($organization_id > 0) {
     $sql = "SELECT COUNT(*) AS count " .
@@ -70,88 +61,66 @@ if ($organization_id > 0) {
     $user->restrict($row['count'] > 0);
 }
 
-// Get the role of the user
-$user->get_role($program_id, $role, $organization);
-
-// Check if the user is the owner of the project
-if ($project_id > 0)
-{
-    $sql = "SELECT COUNT(*) AS count " .
-           "FROM {$db->prefix}projects prj " .
-           "LEFT JOIN {$db->prefix}participants prt " .
-           "ON prj.id = prt.project_id " .
-           "WHERE prj.id = :project_id " .
-           "AND prt.username = :username " .
-           "AND (prt.role = 's' " .
-           "OR (prt.role = 'm' " .
-           "AND prj.is_accepted = 1))";
-    $owner_count = $db->query($sql,
-                              array('project_id' => $project_id,
-                                    'username' => $user->username),
-                              true);
-
-    $is_owner = $owner_count['count'] > 0;
-}
-else
-{
-    $is_owner = false;
-}
+$project_permissions = get_project_permissions($program_data, $role, $project_data);
+$is_owner = $project_permissions->is_owner;
 
 // Serve the page based on the action
 if ($action == 'editor')
 {
     $page_title = $project_id == 0 ? $lang->get('submit_proposal') : $lang->get('edit_project');
 
-    // Program ID is mandatory for editor
-    $user->restrict($program_id > 0);
-
     // Validate pass status of student (should be 1, 0 or -1)
     $user->restrict(in_array($is_passed, array(1, 0, -1)));
 
-    // Only students can create new proposals
     if ($project_id == 0)
-    {
-        $user->restrict($role == 's', true);
-    }
-
-    // Only owners can edit a project
+        $user->restrict($project_permissions->can_submit, true);
     else
-    {
-        $user->restrict($is_owner, true);
-    }
-
-    // Past student deadline, don't let them submit or edit
-    if ($role == 's')
-    {
-        $sql = "SELECT dl_student FROM {$db->prefix}programs " .
-               "WHERE id = ?";
-        $program_data = $db->query($sql, $program_id, true);
-
-        $user->restrict($core->timestamp < $program_data['dl_student'], true);
-    }
+        $user->restrict($project_permissions->can_edit, true);
 
-    // Fetch project data
     if ($project_id > 0)
     {
-        $sql = "SELECT * FROM {$db->prefix}projects prj " .
-               "LEFT JOIN {$db->prefix}participants prt " .
-               "ON prj.id = prt.project_id " .
-               "WHERE prj.id = ? " .
-               "AND prt.role = 's'";
-        $project_data = $db->query($sql, $project_id, true);
-
-        // Do not let anyone but admins edit rejected projects`
-        $user->restrict($project_data['is_accepted'] != 0, true);
-
         // Load data from DB only if new data wasn't POSTed
         if (!$project_save)
         {
+            // See if the student is passed
+            $sql = "SELECT prt.passed FROM {$db->prefix}projects prj " .
+                   "  LEFT JOIN {$db->prefix}participants prt " .
+                   "ON prj.id = prt.project_id " .
+                   "WHERE prj.id = ? " .
+                   "AND prt.role = 's'";
+            $row = $db->query($sql, $project_id, true);
+
             $title = $project_data['title'];
             $description = $project_data['description'];
             $organization_id = $project_data['organization_id'];
-            $is_passed = $project_data['passed'];
+            $is_passed = $row['passed'];
             $is_complete = $project_data['is_complete'];
         }
+
+        if ($organization_id != $project_data['organization_id'])
+        {
+            if (!$project_permissions->can_change_organization)
+            {
+                $organization_change_valid = false;
+            }
+            else
+            {
+                // If the organization can be changed, make sure that the target organization
+                // is taking new proposals.
+                if (!$user->is_admin && $core->timestamp >= $program_data['dl_student'])
+                {
+                    $organization_data = $cache->get_organization_data($project_data['organization_id']);
+                    $organization_change_valid = $organization_data['late_submission'];
+                }
+                else
+                {
+                    $organization_change_valid = true;
+                }
+            }
+
+            if (!$organization_change_valid)
+                $organization_id = $project_data['organization_id'];
+        }
     }
 
     // Only mentor/admins can mark project as complete and pass a student
@@ -358,12 +327,16 @@ if ($action == 'editor')
         'project_title'         => htmlspecialchars($title),
         'project_description'   => nl2br(htmlspecialchars($description)),
         'new_mentor'            => htmlspecialchars($new_mentor),
-        'organization_select'   => build_organization_select($program_id, $organization_id, false),
+        'organization_select'   => build_organization_select($program_id, $organization_id,
+                                                             false,
+                                                             $project_permissions->late_submission,
+                                                             $project_id != 0 && 
!$project_permissions->can_change_organization),
         'success_message'       => isset($success_message) ? $success_message : '',
         'error_message'         => isset($error_message) ? $error_message : '',
         'success_visibility'    => $skin->visibility(empty($success_message), true),
         'error_visibility'      => $skin->visibility(empty($error_message), true),
         'new_visibility'        => $skin->visibility($project_id == 0),
+        'late_submission_visibility' => $skin->visibility($project_id == 0 && 
$project_permissions->late_submission),
         'decision_visibility'   => $skin->visibility($project_id > 0 && $can_decide),
         'subscribe_visibility'  => $skin->visibility(isset($show_subscribe)),
         'newuser_visibility'    => $skin->visibility($project_id > 0 && $user->is_admin),
@@ -419,10 +392,7 @@ else if ($action == 'view')
 {
     // Program and Project IDs are mandatory here
     $user->restrict($program_id > 0 && $project_id > 0);
-
-    // Get program, project and participant data
-    $program_data     = $cache->get_program_data($program_id);
-    $project_data     = $cache->get_project_data($project_id);
+    $user->restrict($project_permissions->can_view);
 
     if ($project_data['organization_id'] != null)
         $organization_data  = $cache->get_organization_data($project_data['organization_id']);
@@ -440,10 +410,6 @@ else if ($action == 'view')
         $cache->put("participant_{$project_id}", $participant_data, 'projects');
     }
 
-    // Now that we have project data, allow only owner, mentors, and admins
-    // to view the project
-    $user->restrict($role == 'm' || $is_owner, true);
-
     // Only allow mentors and admins to view mentorship unless project is accepted
     $can_view_mentor = $project_data['is_accepted'] == 1 || $user->is_admin || $role == 'm';
 
@@ -497,12 +463,6 @@ else if ($action == 'view')
         $result = $lang->get('undecided');
     }
 
-    // Don't let students edit post student deadline or if project is rejected
-    if ($role == 's' && $is_owner)
-    {
-        $is_owner = ($core->timestamp < $program_data['dl_student'] && $project_data['is_accepted'] != 0);
-    }
-
     // Only mentors and admins can view proposal status before mentor deadline
     if ($role != 'm' && !$user->is_admin && $core->timestamp < $program_data['dl_mentor'])
     {
@@ -622,13 +582,14 @@ else if ($action == 'view')
         'success_message'           => isset($success_message) ? $success_message : '',
         'success_visibility'        => $skin->visibility(empty($success_message), true),
         'mentor_visibility'         => $skin->visibility($can_view_mentor),
-        'edit_visibility'           => $skin->visibility($is_owner || $user->is_admin),
-        'attach_visibility'         => $skin->visibility($is_owner),
+        'edit_visibility'           => $skin->visibility($project_permissions->can_edit),
+        'attach_visibility'         => $skin->visibility($is_owner && $project_permissions->can_edit),
         'attachments_visibility'    => $skin->visibility(count($attachment_data) > 0),
         'delete_visibility'         => $skin->visibility($user->is_admin),
         'mentor_project_visibility' => $skin->visibility($can_mentor),
         'mentor_remove_visibility'  => $skin->visibility($can_remove_mentor),
-        'actions_visibility'        => $skin->visibility($is_owner || $can_mentor || $can_remove_mentor || 
$user->is_admin),
+        'actions_visibility'        => $skin->visibility(($is_owner && $project_permissions->can_edit) ||
+                                                         $can_mentor || $can_remove_mentor || 
$user->is_admin),
         'subscribe_visibility'      => $skin->visibility(isset($show_subscribe)),
         'approve_visibility'        => $skin->visibility($can_approve),
         'reject_visibility'         => $skin->visibility($can_reject),
@@ -647,9 +608,6 @@ else if ($action == 'user' || $action == 'proposed' || $action == 'accepted' ||
     // Program ID is mandatory here
     $user->restrict($program_id > 0);
 
-    // Get program  data
-    $program_data = $cache->get_program_data($program_id);
-
     $params = array('program_id' => $program_id,
                     'username' => $user->username,
                     'i:start' => $limit_start,
@@ -856,9 +814,6 @@ else if ($action == 'apply')
                         'organization_id' => $organization_id > 0 ? $organization_id : null,
                         'username' => $user->username);
 
-        // Get the program data
-        $program_data = $cache->get_program_data($program_id);
-
         // Set the new role based on action
         $new_role = $category == 'student' ? 's' : 'i';
 
@@ -896,7 +851,7 @@ else if ($action == 'apply')
         $core->redirect("?q=program_home&prg={$program_id}");
     }
 
-    $organization_select = build_organization_select($program_id, 0, true);
+    $organization_select = build_organization_select($program_id, 0, true, false);
 
     // Assign final skin data
     $skin->assign(array(
diff --git a/skins/easterngreen/html/tpl_program_home.html b/skins/easterngreen/html/tpl_program_home.html
index 3ab6545..d87f0da 100644
--- a/skins/easterngreen/html/tpl_program_home.html
+++ b/skins/easterngreen/html/tpl_program_home.html
@@ -96,6 +96,9 @@
                 {{view_submissions}}
             </a>
 
+            <div class="alert alert-info [[late_submission_visibility]]">
+              {{late_submission_notice}}
+            </div>
             <div class="resign-block">
                 <a href="?q=view_projects&amp;prg=[[program_id]]&amp;a=resign" class="btn btn-danger 
btn-mini">
                     {{resign_program}}
diff --git a/skins/easterngreen/html/tpl_view_projects_editor.html 
b/skins/easterngreen/html/tpl_view_projects_editor.html
index 5ca1420..8340cc5 100644
--- a/skins/easterngreen/html/tpl_view_projects_editor.html
+++ b/skins/easterngreen/html/tpl_view_projects_editor.html
@@ -30,6 +30,10 @@
   </p>
 </div>
 
+<div class="alert alert-info [[late_submission_visibility]]">
+    {{late_submission_notice}}
+</div>
+
 <div class="control-group">
     <label class="control-label">{{project_title}}</label>
     <div class="controls">
diff --git a/utils.php b/utils.php
index c6f6b12..a777a76 100644
--- a/utils.php
+++ b/utils.php
@@ -1,5 +1,6 @@
 <?php
-function build_organization_select($program_id, $current, $include_other, $name="o", $id=null)
+function build_organization_select($program_id, $current, $include_other, $only_late,
+                                   $disabled=false, $name="o", $id=null)
 {
     global $db, $lang;
 
@@ -11,8 +12,10 @@ function build_organization_select($program_id, $current, $include_other, $name=
     $option_name = $lang->get('select_organization');
     $n = ($name !== null) ? " name='{$name}'" : '';
     $i = ($id !== null) ? " id='{$id}'" : '';
+    $d = $disabled ? " disabled" : '';
 
-    $organization_select = "<select$n$i>";
+    echo $disabled;
+    $organization_select = "<select$n$i$d>";
 
     $selected = $current == 0 ? " selected" : "";
     $organization_select .= "<option value='0'$selected>{$option_name}</option>";
@@ -22,7 +25,8 @@ function build_organization_select($program_id, $current, $include_other, $name=
         $organization_title = htmlspecialchars($row['title']);
         $id = $row['id'];
         $selected = $current == $id ? " selected" : "";
-        $organization_select .= "<option value='$id'$selected>{$organization_title}</option>";
+        $d = ($only_late && $row['late_submission'] == 0) ? " disabled" : "";
+        $organization_select .= "<option value='$id'$selected$d>{$organization_title}</option>";
       }
 
     $option_name = $lang->get('other');
@@ -61,4 +65,103 @@ function opinion_is_valid($value) {
   return in_array($value, $VALID_OPINIONS);
 }
 
+class ProjectPermissions {
+
+    public $can_submit;
+    public $late_submission;
+    public $is_owner;
+    public $can_view;
+    public $can_edit;
+    public $can_change_organization;
+
+    function __construct($program_data, $role, $project_data) {
+        global $cache, $core, $db, $user;
+
+        $can_submit = false;
+        $late_submission = false;
+        $is_owner = false;
+        $can_view = false;
+        $can_edit = false;
+        $can_change_organization = false;
+
+        if ($role == 's') {
+            $can_submit = true;
+
+            if ($core->timestamp >= $program_data['dl_student'])
+            {
+                $can_submit = false;
+
+                if ($core->timestamp < $program_data['dl_mentor']) {
+                    $sql = "SELECT COUNT(*) AS count from {$db->prefix}organizations " .
+                               "WHERE program_id = ? AND late_submission = 1";
+                    $row = $db->query($sql, $program_data['id'], true);
+                    $late_submission = $row['count'] > 0;
+                    if ($late_submission)
+                        $can_submit = true;
+                }
+            }
+        }
+
+        if ($project_data !== null) {
+            $sql = "SELECT COUNT(*) AS count " .
+                   "FROM {$db->prefix}projects prj " .
+                   "LEFT JOIN {$db->prefix}participants prt " .
+                   "ON prj.id = prt.project_id " .
+                   "WHERE prj.id = :project_id " .
+                   "AND prt.username = :username " .
+                   "AND (prt.role = 's' " .
+                   "OR (prt.role = 'm' " .
+                   "AND prj.is_accepted = 1))";
+            $row = $db->query($sql,
+                              array('project_id' => $project_data['id'],
+                                    'username' => $user->username),
+                              true);
+
+            $is_owner = $row['count'] > 0;
+
+            $can_view = $user->is_admin || $role =='m' || $is_owner;
+
+            if ($user->is_admin) {
+                $can_edit = true;
+                $can_change_organization = true;
+            } else if ($is_owner && $role == 's') {
+                $can_edit = true;
+                $can_change_organization = true;
+
+                // Do not let anyone but admins edit rejected projects`
+                if ($project_data['is_accepted'] == 0) {
+                    $can_edit = false;
+                    $can_change_organization = false;
+                } else if ($core->timestamp >= $program_data['dl_mentor']) {
+                    // Past the selection deadline, students cannot edit anything
+                    $can_edit = false;
+                    $can_change_organization = false;
+                } else if ($core->timestamp > $program_data['dl_student']) {
+                    // Projects submitted to organizations that are no longer taking
+                    // new submissions can't be moved by the student to a different
+                    // organization, because they can't be moved back.
+
+                    if (!$can_submit) {
+                        $can_change_organization = false;
+                    } else if ($late_submission) {
+                        $organization_data = $cache->get_organization_data($project_data['organization_id']);
+                        $can_change_organization = $organization_data['late_submission'] != 0;
+                    }
+                }
+            }
+        }
+
+        $this->can_submit = $can_submit;
+        $this->late_submission = $late_submission;
+        $this->is_owner = $is_owner;
+        $this->can_view = $can_view;
+        $this->can_edit = $can_edit;
+        $this->can_change_organization = $can_change_organization;
+    }
+}
+
+function get_project_permissions($program_data, $role, $project_data) {
+    return new ProjectPermissions($program_data, $role, $project_data);
+}
+
 ?>
\ No newline at end of file


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]