Skip to content

Commit

Permalink
MDL-51965 tool_lp: Revisit capabilities to read/manage plans
Browse files Browse the repository at this point in the history
Templates can no longer be linked to the plan via the plan editing
page because we will need to handle plan linking/unlinking directly.
  • Loading branch information
Frederic Massart committed Apr 18, 2016
1 parent 6d57a98 commit 5159d67
Show file tree
Hide file tree
Showing 13 changed files with 469 additions and 159 deletions.
108 changes: 32 additions & 76 deletions admin/tool/lp/classes/api.php
Expand Up @@ -1199,24 +1199,25 @@ public static function reorder_template_competency($templateid, $competencyidfro
*/
public static function list_user_plans($userid) {
global $USER;

$select = 'userid = :userid';
$params = array('userid' => $userid);

$context = context_user::instance($userid);

// We can allow guest user to pass they will not have LP.
if ($USER->id != $userid) {
require_capability('tool/lp:planview', $context);
} else {
require_capability('tool/lp:planviewown', $context);
// Check that we can read something here.
if (!plan::can_read_user($userid) && !plan::can_read_user_draft($userid)) {
throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', '');
}

// Users that can manage plans can only see active and completed plans.
if (!has_any_capability(array('tool/lp:planmanage', 'tool/lp:planmanageown', 'tool/lp:plancreateown'), $context)) {
// The user cannot view the drafts.
if (!plan::can_read_user_draft($userid)) {
$select = ' AND status != :statusdraft';
$params['statusdraft'] = plan::STATUS_DRAFT;
}
// The user cannot view the non-drafts.
if (!plan::can_read_user($userid)) {
$select = ' AND status = :statusdraft';
$params['statusdraft'] = plan::STATUS_DRAFT;
}

return plan::get_records_select($select, $params, 'timemodified DESC');
}
Expand All @@ -1229,30 +1230,13 @@ public static function list_user_plans($userid) {
*/
public static function create_plan(stdClass $record) {
global $USER;
$plan = new plan(0, $record);

$context = context_user::instance($record->userid);

$manageplans = has_capability('tool/lp:planmanage', $context);
$createdraft = has_capability('tool/lp:plancreateown', $context);
$manageownplan = has_capability('tool/lp:planmanageown', $context);

// Any of them is enough.
if ($USER->id == $record->userid && !$manageplans && !$createdraft && !$manageownplan) {
// Exception about plancreateown as it is the one that is closer to basic users.
throw new required_capability_exception($context, 'tool/lp:plancreateown', 'nopermissions', '');
} else if ($USER->id != $record->userid && !$manageplans) {
if (!$plan->can_manage()) {
$context = context_user::instance($plan->get_userid());
throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', '');
}

if (!isset($record->status)) {
// Default to status draft.
$record->status = plan::STATUS_DRAFT;
} else if ($record->status !== plan::STATUS_DRAFT && !$manageplans && !$manageownplan) {
// If the user can only create drafts we don't allow them to set other status.
throw new required_capability_exception($context, 'tool/lp:planmanageown', 'nopermissions', '');
}

$plan = new plan(0, $record);
$plan->create();
return $plan;
}
Expand All @@ -1265,37 +1249,28 @@ public static function create_plan(stdClass $record) {
*/
public static function update_plan(stdClass $record) {
global $USER;
$plan = new plan($record->id);

$context = context_user::instance($record->userid);

$manageplans = has_capability('tool/lp:planmanage', $context);
$createdraft = has_capability('tool/lp:plancreateown', $context);
$manageownplan = has_capability('tool/lp:planmanageown', $context);

// Any of them is enough.
if ($USER->id == $record->userid && !$manageplans && !$createdraft && !$manageownplan) {
throw new required_capability_exception($context, 'tool/lp:planmanageown', 'nopermissions', '');
} else if (!$manageplans) {
// Validate that the plan as it is can be managed.
if (!$plan->can_manage()) {
$context = context_user::instance($plan->get_userid());
throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', '');
}

$plan = new plan($record->id);

// We don't allow users without planmanage and without
// planmanageown to edit plans that other users modified.
if (!$manageplans && !$manageownplan && $USER->id != $plan->get_usermodified()) {
throw new \moodle_exception('erroreditingmodifiedplan', 'tool_lp');
} else if (!$manageplans && $USER->id != $plan->get_userid()) {
throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', '');
// Prevent change of ownership as the capabilities are checked against that.
if (isset($record->userid) && $plan->get_userid() != $record->userid) {
throw new coding_exception('A plan cannot be transfered to another user');
}
$plan->from_record($record);

// If the user can only create drafts we don't allow them to set other status.
if ($record->status !== plan::STATUS_DRAFT && !$manageplans && !$manageownplan) {
throw new required_capability_exception($context, 'tool/lp:planmanageown', 'nopermissions', '');
// Revalidate after the data has be injected. This handles status change, etc...
if (!$plan->can_manage()) {
$context = context_user::instance($plan->get_userid());
throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', '');
}

$plan->from_record($record);
return $plan->update();
$plan->update();
return $plan;
}

/**
Expand All @@ -1306,22 +1281,13 @@ public static function update_plan(stdClass $record) {
*/
public static function read_plan($id) {
global $USER;

$plan = new plan($id);
$context = context_user::instance($plan->get_userid());

if ($USER->id == $plan->get_userid()) {
require_capability('tool/lp:planviewown', $context);
} else {
require_capability('tool/lp:planview', $context);
if (!$plan->can_read()) {
$context = context_user::instance($plan->get_userid());
throw new required_capability_exception($context, 'tool/lp:planview', 'nopermissions', '');
}

// We require any of these capabilities to retrieve draft plans.
if ($plan->get_status() == plan::STATUS_DRAFT &&
!has_any_capability(array('tool/lp:planmanageown', 'tool/lp:planmanage', 'tool/lp:plancreateown'), $context)) {
// Exception about plancreateown as it is the one that is closer to basic users.
throw new required_capability_exception($context, 'tool/lp:plancreateown', 'nopermissions', '');
}
return $plan;
}

Expand All @@ -1333,20 +1299,10 @@ public static function read_plan($id) {
*/
public static function delete_plan($id) {
global $USER;

$plan = new plan($id);

$context = context_user::instance($plan->get_userid());

$manageplans = has_capability('tool/lp:planmanage', $context);
$manageownplan = has_capability('tool/lp:planmanageown', $context);

if ($USER->id == $plan->get_userid() && $USER->id != $plan->get_usermodified() &&
!$manageplans && !$manageownplan) {
// A normal user can only edit its plan if they created it.
throw new required_capability_exception($context, 'tool/lp:planmanageown', 'nopermissions', '');
} else if ($USER->id != $plan->get_userid() && !$manageplans) {
// Other users needs to have tool/lp:planmanage.
if (!$plan->can_manage()) {
$context = context_user::instance($plan->get_userid());
throw new required_capability_exception($context, 'tool/lp:planmanage', 'nopermissions', '');
}

Expand Down
6 changes: 3 additions & 3 deletions admin/tool/lp/classes/external.php
Expand Up @@ -3099,7 +3099,7 @@ public static function create_plan($name, $description, $descriptionformat, $use
$result = api::create_plan($params);
$record = $result->to_record();
$record->statusname = $result->get_statusname();
$record->usercanupdate = $result->can_update();
$record->usercanupdate = $result->can_manage();
return external_api::clean_returnvalue(self::create_plan_returns(), $record);
}

Expand Down Expand Up @@ -3207,7 +3207,7 @@ public static function update_plan($id, $name, $description, $descriptionformat,
$result = api::update_plan($params);
$record = $result->to_record();
$record->statusname = $result->get_statusname();
$record->usercanupdate = $result->can_update();
$record->usercanupdate = $result->can_manage();
return external_api::clean_returnvalue(self::update_plan_returns(), $record);
}

Expand Down Expand Up @@ -3249,7 +3249,7 @@ public static function read_plan($id) {
$result = api::read_plan($params['id']);
$record = $result->to_record();
$record->statusname = $result->get_statusname();
$record->usercanupdate = $result->can_update();
$record->usercanupdate = $result->can_manage();
return external_api::clean_returnvalue(self::read_plan_returns(), $record);
}

Expand Down
6 changes: 0 additions & 6 deletions admin/tool/lp/classes/form/plan.php
Expand Up @@ -59,12 +59,6 @@ public function definition() {
$mform->addElement('editor', 'description', get_string('plandescription', 'tool_lp'), array('rows' => 4));
$mform->setType('description', PARAM_TEXT);

$templates = $this->get_template_options();
if ($templates) {
$mform->addElement('select', 'templateid', get_string('plantemplate', 'tool_lp'), $templates);
$mform->addHelpButton('templateid', 'plantemplate', 'tool_lp');
}

$mform->addElement('date_selector', 'duedate', get_string('duedate', 'tool_lp'));
$mform->addHelpButton('duedate', 'duedate', 'tool_lp');

Expand Down
2 changes: 1 addition & 1 deletion admin/tool/lp/classes/output/plans_page.php
Expand Up @@ -88,7 +88,7 @@ public function export_for_template(renderer_base $output) {
foreach ($this->plans as $plan) {
$record = $plan->to_record();
$record->statusname = $plan->get_statusname();
$record->usercanupdate = $plan->can_update();
$record->usercanupdate = $plan->can_manage();
$data->plans[] = $record;
}
}
Expand Down
112 changes: 90 additions & 22 deletions admin/tool/lp/classes/plan.php
Expand Up @@ -86,33 +86,27 @@ protected static function define_properties() {
}

/**
* Whether the current user can update the learning plan.
* Whether the current user can manage the plan.
*
* @return bool|null
* @return bool
*/
public function can_update() {
global $USER;

// Null if the record has not been filled.
if (!$userid = $this->get_userid()) {
return null;
}

$context = context_user::instance($userid);

// Not all users can edit all plans, the template should know about it.
if (has_capability('tool/lp:planmanage', $context) ||
has_capability('tool/lp:planmanageown', $context)) {
return true;
public function can_manage() {
if ($this->get_status() == self::STATUS_DRAFT) {
return self::can_manage_user_draft($this->get_userid());
}
return self::can_manage_user($this->get_userid());
}

// The user that created the template can also edit it if he was the last one that modified it. But
// can't do it if it is already completed.
if ($USER->id == $userid && $this->get_usermodified() == $USER->id && $this->get_status() != self::STATUS_COMPLETE) {
return true;
/**
* Whether the current user can read the plan.
*
* @return bool
*/
public function can_read() {
if ($this->get_status() == self::STATUS_DRAFT) {
return self::can_read_user_draft($this->get_userid());
}

return false;
return self::can_read_user($this->get_userid());
}

/**
Expand Down Expand Up @@ -158,4 +152,78 @@ protected function validate_templateid($value) {
return true;
}

/**
* Can the current user manage a user's plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_manage_user($planuserid) {
global $USER;
$context = context_user::instance($planuserid);

$capabilities = array('tool/lp:planmanage');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'tool/lp:planmanageown';
}

return has_any_capability($capabilities, $context);
}

/**
* Can the current user manage a user's draft plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_manage_user_draft($planuserid) {
global $USER;
$context = context_user::instance($planuserid);

$capabilities = array('tool/lp:planmanagedraft');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'tool/lp:planmanageowndraft';
}

return has_any_capability($capabilities, $context);
}

/**
* Can the current user view a user's plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_read_user($planuserid) {
global $USER;
$context = context_user::instance($planuserid);

$capabilities = array('tool/lp:planview');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'tool/lp:planviewown';
}

return has_any_capability($capabilities, $context)
|| self::can_manage_user($planuserid);
}

/**
* Can the current user view a user's draft plan?
*
* @param int $planuserid The user to whom the plan would belong.
* @return bool
*/
public static function can_read_user_draft($planuserid) {
global $USER;
$context = context_user::instance($planuserid);

$capabilities = array('tool/lp:planviewdraft');
if ($context->instanceid == $USER->id) {
$capabilities[] = 'tool/lp:planviewowndraft';
}

return has_any_capability($capabilities, $context)
|| self::can_manage_user_draft($planuserid);
}

}

0 comments on commit 5159d67

Please sign in to comment.