diff --git a/mod/data/lang/en/data.php b/mod/data/lang/en/data.php index 8a44dc36b76fa..d676fc1fbfc2b 100644 --- a/mod/data/lang/en/data.php +++ b/mod/data/lang/en/data.php @@ -32,6 +32,7 @@ $string['alttext'] = 'Alternative text'; $string['approve'] = 'Approve'; $string['approved'] = 'Approved'; +$string['areacontent'] = 'Fields'; $string['ascending'] = 'Ascending'; $string['asearchtemplate'] = 'Advanced search template'; $string['atmaxentry'] = 'You have entered the maximum number of entries allowed!'; diff --git a/mod/data/lib.php b/mod/data/lib.php index 7e8fde0cbfd7d..48c2880fd192f 100644 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -2800,6 +2800,10 @@ function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0) return $exportdata; } +//////////////////////////////////////////////////////////////////////////////// +// File API // +//////////////////////////////////////////////////////////////////////////////// + /** * Lists all browsable file areas * @@ -2811,8 +2815,7 @@ function data_get_exportdata($dataid, $fields, $selectedfields, $currentgroup=0) * @return array */ function data_get_file_areas($course, $cm, $context) { - $areas = array(); - return $areas; + return array('content' => get_string('areacontent', 'mod_data')); } /** @@ -2836,55 +2839,65 @@ function mod_data_get_file_info($browser, $areas, $course, $cm, $context, $filea return null; } - if ($filearea === 'content') { - if (!$content = $DB->get_record('data_content', array('id'=>$itemid))) { - return null; - } + if (!isset($areas[$filearea])) { + return null; + } - if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) { - return null; - } + if (!has_capability('moodle/course:managefiles', $context)) { + return null; + } - if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) { - return null; - } + if (is_null($itemid)) { + require_once($CFG->dirroot.'/mod/data/locallib.php'); + return new data_file_info_container($browser, $course, $cm, $context, $areas, $filearea); + } - if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) { - return null; - } + if (!$content = $DB->get_record('data_content', array('id'=>$itemid))) { + return null; + } - //check if approved - if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) { - return null; - } + if (!$field = $DB->get_record('data_fields', array('id'=>$content->fieldid))) { + return null; + } - // group access - if ($record->groupid) { - $groupmode = groups_get_activity_groupmode($cm, $course); - if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { - if (!groups_is_member($record->groupid)) { - return null; - } + if (!$record = $DB->get_record('data_records', array('id'=>$content->recordid))) { + return null; + } + + if (!$data = $DB->get_record('data', array('id'=>$field->dataid))) { + return null; + } + + //check if approved + if ($data->approval and !$record->approved and !data_isowner($record) and !has_capability('mod/data:approve', $context)) { + return null; + } + + // group access + if ($record->groupid) { + $groupmode = groups_get_activity_groupmode($cm, $course); + if ($groupmode == SEPARATEGROUPS and !has_capability('moodle/site:accessallgroups', $context)) { + if (!groups_is_member($record->groupid)) { + return null; } } + } - $fieldobj = data_get_field($field, $data, $cm); + $fieldobj = data_get_field($field, $data, $cm); - $filepath = is_null($filepath) ? '/' : $filepath; - $filename = is_null($filename) ? '.' : $filename; - if (!$fieldobj->file_ok($filepath.$filename)) { - return null; - } + $filepath = is_null($filepath) ? '/' : $filepath; + $filename = is_null($filename) ? '.' : $filename; + if (!$fieldobj->file_ok($filepath.$filename)) { + return null; + } - $fs = get_file_storage(); - if (!($storedfile = $fs->get_file($context->id, 'mod_data', $filearea, $itemid, $filepath, $filename))) { - return null; - } - $urlbase = $CFG->wwwroot.'/pluginfile.php'; - return new file_info_stored($browser, $context, $storedfile, $urlbase, $filearea, $itemid, true, true, false); + $fs = get_file_storage(); + if (!($storedfile = $fs->get_file($context->id, 'mod_data', $filearea, $itemid, $filepath, $filename))) { + return null; } + $urlbase = $CFG->wwwroot.'/pluginfile.php'; - return null; + return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); } /** diff --git a/mod/data/locallib.php b/mod/data/locallib.php index d8f8ab90765ac..4ab3df8e4ab13 100644 --- a/mod/data/locallib.php +++ b/mod/data/locallib.php @@ -398,3 +398,119 @@ public function get_allowed_export_config() { return array('mineonly'); } } + + +/** + * Class representing the virtual node with all itemids in the file browser + * + * @category files + * @copyright 2012 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class data_file_info_container extends file_info { + /** @var file_browser */ + protected $browser; + /** @var stdClass */ + protected $course; + /** @var stdClass */ + protected $cm; + /** @var string */ + protected $component; + /** @var stdClass */ + protected $context; + /** @var array */ + protected $areas; + /** @var string */ + protected $filearea; + + /** + * Constructor (in case you did not realize it ;-) + * + * @param file_browser $browser + * @param stdClass $course + * @param stdClass $cm + * @param stdClass $context + * @param array $areas + * @param string $filearea + */ + public function __construct($browser, $course, $cm, $context, $areas, $filearea) { + parent::__construct($browser, $context); + $this->browser = $browser; + $this->course = $course; + $this->cm = $cm; + $this->component = 'mod_data'; + $this->context = $context; + $this->areas = $areas; + $this->filearea = $filearea; + } + + /** + * @return array with keys contextid, filearea, itemid, filepath and filename + */ + public function get_params() { + return array( + 'contextid' => $this->context->id, + 'component' => $this->component, + 'filearea' => $this->filearea, + 'itemid' => null, + 'filepath' => null, + 'filename' => null, + ); + } + + /** + * Can new files or directories be added via the file browser + * + * @return bool + */ + public function is_writable() { + return false; + } + + /** + * Should this node be considered as a folder in the file browser + * + * @return bool + */ + public function is_directory() { + return true; + } + + /** + * Returns localised visible name of this node + * + * @return string + */ + public function get_visible_name() { + return $this->areas[$this->filearea]; + } + + /** + * Returns list of children nodes + * + * @return array of file_info instances + */ + public function get_children() { + global $DB; + + $children = array(); + $itemids = $DB->get_records('files', array('contextid' => $this->context->id, 'component' => $this->component, + 'filearea' => $this->filearea), 'itemid DESC', "DISTINCT itemid"); + foreach ($itemids as $itemid => $unused) { + if ($child = $this->browser->get_file_info($this->context, 'mod_data', $this->filearea, $itemid)) { + $children[] = $child; + } + } + + return $children; + } + + /** + * Returns parent file_info instance + * + * @return file_info or null for root + */ + public function get_parent() { + return $this->browser->get_file_info($this->context); + } +} diff --git a/mod/forum/lang/en/forum.php b/mod/forum/lang/en/forum.php index 9b9142f417893..48e5d76a92528 100644 --- a/mod/forum/lang/en/forum.php +++ b/mod/forum/lang/en/forum.php @@ -35,6 +35,8 @@ $string['allunsubscribe'] = 'Unsubscribe from all forums'; $string['alreadyfirstpost'] = 'This is already the first post in the discussion'; $string['anyfile'] = 'Any file'; +$string['areaattachment'] = 'Attachments'; +$string['areapost'] = 'Messages'; $string['attachment'] = 'Attachment'; $string['attachment_help'] = 'You can optionally attach one or more files to a forum post. If you attach an image, it will be displayed after the message.'; $string['attachmentnopost'] = 'You cannot export attachments without a post id'; diff --git a/mod/forum/lib.php b/mod/forum/lib.php index a3bb15d916951..d3b609e6588da 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -3958,6 +3958,10 @@ function forum_print_attachments($post, $cm, $type) { } } +//////////////////////////////////////////////////////////////////////////////// +// File API // +//////////////////////////////////////////////////////////////////////////////// + /** * Lists all browsable file areas * @@ -3969,8 +3973,10 @@ function forum_print_attachments($post, $cm, $type) { * @return array */ function forum_get_file_areas($course, $cm, $context) { - $areas = array(); - return $areas; + return array( + 'attachment' => get_string('areaattachment', 'mod_forum'), + 'post' => get_string('areapost', 'mod_forum'), + ); } /** @@ -3996,11 +4002,28 @@ function forum_get_file_info($browser, $areas, $course, $cm, $context, $filearea return null; } - $fileareas = array('attachment', 'post'); - if (!in_array($filearea, $fileareas)) { + // filearea must contain a real area + if (!isset($areas[$filearea])) { return null; } + // this is enforced by {@link file_info_context_course} currently + if (!has_capability('moodle/course:managefiles', $context)) { + return null; + } + + // Note that forum_user_can_see_post() additionally allows access for parent roles + // and it explicitly checks qanda forum type, too. One day, when we stop requiring + // course:managefiles, we will need to extend this. + if (!has_capability('mod/forum:viewdiscussion', $context)) { + return null; + } + + if (is_null($itemid)) { + require_once($CFG->dirroot.'/mod/forum/locallib.php'); + return new forum_file_info_container($browser, $course, $cm, $context, $areas, $filearea); + } + if (!$post = $DB->get_record('forum_posts', array('id' => $itemid))) { return null; } @@ -4021,14 +4044,12 @@ function forum_get_file_info($browser, $areas, $course, $cm, $context, $filearea } // Make sure groups allow this user to see this file - if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used - if (!groups_group_exists($discussion->groupid)) { // Can't find group - return null; // Be safe and don't send it to anyone - } - - if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { - // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS - return null; + if ($discussion->groupid > 0) { + $groupmode = groups_get_activity_groupmode($cm, $course); + if ($groupmode == SEPARATEGROUPS) { + if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { + return null; + } } } @@ -4038,7 +4059,7 @@ function forum_get_file_info($browser, $areas, $course, $cm, $context, $filearea } $urlbase = $CFG->wwwroot.'/pluginfile.php'; - return new file_info_stored($browser, $context, $storedfile, $urlbase, $filearea, $itemid, true, true, false); + return new file_info_stored($browser, $context, $storedfile, $urlbase, $itemid, true, true, false, false); } /** @@ -4064,8 +4085,10 @@ function forum_pluginfile($course, $cm, $context, $filearea, $args, $forcedownlo require_course_login($course, true, $cm); - $fileareas = array('attachment', 'post'); - if (!in_array($filearea, $fileareas)) { + $areas = forum_get_file_areas($course, $cm, $context); + + // filearea must contain a real area + if (!isset($areas[$filearea])) { return false; } @@ -4091,14 +4114,12 @@ function forum_pluginfile($course, $cm, $context, $filearea, $args, $forcedownlo } // Make sure groups allow this user to see this file - if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) { // Groups are being used - if (!groups_group_exists($discussion->groupid)) { // Can't find group - return false; // Be safe and don't send it to anyone - } - - if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { - // do not send posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS - return false; + if ($discussion->groupid > 0) { + $groupmode = groups_get_activity_groupmode($cm, $course); + if ($groupmode == SEPARATEGROUPS) { + if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) { + return false; + } } } diff --git a/mod/forum/locallib.php b/mod/forum/locallib.php index a14038056cc42..dd1b3affb32cb 100644 --- a/mod/forum/locallib.php +++ b/mod/forum/locallib.php @@ -391,3 +391,119 @@ public static function base_supported_formats() { return array(PORTFOLIO_FORMAT_FILE, PORTFOLIO_FORMAT_RICHHTML, PORTFOLIO_FORMAT_PLAINHTML, PORTFOLIO_FORMAT_LEAP2A); } } + + +/** + * Class representing the virtual node with all itemids in the file browser + * + * @category files + * @copyright 2012 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class forum_file_info_container extends file_info { + /** @var file_browser */ + protected $browser; + /** @var stdClass */ + protected $course; + /** @var stdClass */ + protected $cm; + /** @var string */ + protected $component; + /** @var stdClass */ + protected $context; + /** @var array */ + protected $areas; + /** @var string */ + protected $filearea; + + /** + * Constructor (in case you did not realize it ;-) + * + * @param file_browser $browser + * @param stdClass $course + * @param stdClass $cm + * @param stdClass $context + * @param array $areas + * @param string $filearea + */ + public function __construct($browser, $course, $cm, $context, $areas, $filearea) { + parent::__construct($browser, $context); + $this->browser = $browser; + $this->course = $course; + $this->cm = $cm; + $this->component = 'mod_forum'; + $this->context = $context; + $this->areas = $areas; + $this->filearea = $filearea; + } + + /** + * @return array with keys contextid, filearea, itemid, filepath and filename + */ + public function get_params() { + return array( + 'contextid' => $this->context->id, + 'component' => $this->component, + 'filearea' => $this->filearea, + 'itemid' => null, + 'filepath' => null, + 'filename' => null, + ); + } + + /** + * Can new files or directories be added via the file browser + * + * @return bool + */ + public function is_writable() { + return false; + } + + /** + * Should this node be considered as a folder in the file browser + * + * @return bool + */ + public function is_directory() { + return true; + } + + /** + * Returns localised visible name of this node + * + * @return string + */ + public function get_visible_name() { + return $this->areas[$this->filearea]; + } + + /** + * Returns list of children nodes + * + * @return array of file_info instances + */ + public function get_children() { + global $DB; + + $children = array(); + $itemids = $DB->get_records('files', array('contextid' => $this->context->id, 'component' => $this->component, + 'filearea' => $this->filearea), 'itemid DESC', "DISTINCT itemid"); + foreach ($itemids as $itemid => $unused) { + if ($child = $this->browser->get_file_info($this->context, 'mod_forum', $this->filearea, $itemid)) { + $children[] = $child; + } + } + + return $children; + } + + /** + * Returns parent file_info instance + * + * @return file_info or null for root + */ + public function get_parent() { + return $this->browser->get_file_info($this->context); + } +} diff --git a/mod/glossary/lang/en/glossary.php b/mod/glossary/lang/en/glossary.php index 826a9661c9769..a8d67f9f35604 100644 --- a/mod/glossary/lang/en/glossary.php +++ b/mod/glossary/lang/en/glossary.php @@ -44,6 +44,8 @@ $string['approve'] = 'Approve'; $string['approvaldisplayformat'] = 'Approval display format'; $string['approvaldisplayformat_help'] = 'When approving glossary items you may wish to use a different display format'; +$string['areaattachment'] = 'Attachments'; +$string['areaentry'] = 'Definitions'; $string['areyousuredelete'] = 'Are you sure you want to delete this entry?'; $string['areyousuredeletecomment'] = 'Are you sure you want to delete this comment?'; $string['areyousureexport'] = 'Are you sure you want to export this entry to'; diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php index 993c8ea17019d..8e25256829ea5 100644 --- a/mod/glossary/lib.php +++ b/mod/glossary/lib.php @@ -1585,6 +1585,10 @@ function glossary_print_attachments($entry, $cm, $type=NULL, $align="left") { } } +//////////////////////////////////////////////////////////////////////////////// +// File API // +//////////////////////////////////////////////////////////////////////////////// + /** * Lists all browsable file areas * @@ -1596,8 +1600,10 @@ function glossary_print_attachments($entry, $cm, $type=NULL, $align="left") { * @return array */ function glossary_get_file_areas($course, $cm, $context) { - $areas = array(); - return $areas; + return array( + 'attachment' => get_string('areaattachment', 'mod_glossary'), + 'entry' => get_string('areaentry', 'mod_glossary'), + ); } /** @@ -1621,42 +1627,52 @@ function mod_glossary_get_file_info($browser, $areas, $course, $cm, $context, $f return null; } - if ($filearea === 'attachment' or $filearea === 'entry') { - if (!$entry = $DB->get_record('glossary_entries', array('id' => $itemid))) { - return null; - } + if (!isset($areas[$filearea])) { + return null; + } - if (!$glossary = $DB->get_record('glossary', array('id' => $cm->instance))) { - return null; - } + if (!has_capability('moodle/course:managefiles', $context)) { + return null; + } - if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) { - return null; - } + if (is_null($itemid)) { + require_once($CFG->dirroot.'/mod/glossary/locallib.php'); + return new glossary_file_info_container($browser, $course, $cm, $context, $areas, $filearea); + } - // this trickery here is because we need to support source glossary access - if ($entry->glossaryid == $cm->instance) { - $filecontext = $context; - } else if ($entry->sourceglossaryid == $cm->instance) { - if (!$maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) { - return null; - } - $filecontext = get_context_instance(CONTEXT_MODULE, $maincm->id); - } else { - return null; - } + if (!$entry = $DB->get_record('glossary_entries', array('id' => $itemid))) { + return null; + } - $fs = get_file_storage(); - $filepath = is_null($filepath) ? '/' : $filepath; - $filename = is_null($filename) ? '.' : $filename; - if (!($storedfile = $fs->get_file($filecontext->id, 'mod_glossary', $filearea, $itemid, $filepath, $filename))) { + if (!$glossary = $DB->get_record('glossary', array('id' => $cm->instance))) { + return null; + } + + if ($glossary->defaultapproval and !$entry->approved and !has_capability('mod/glossary:approve', $context)) { + return null; + } + + // this trickery here is because we need to support source glossary access + if ($entry->glossaryid == $cm->instance) { + $filecontext = $context; + } else if ($entry->sourceglossaryid == $cm->instance) { + if (!$maincm = get_coursemodule_from_instance('glossary', $entry->glossaryid)) { return null; } - $urlbase = $CFG->wwwroot.'/pluginfile.php'; - return new file_info_stored($browser, $filecontext, $storedfile, $urlbase, $filearea, $itemid, true, true, false); + $filecontext = get_context_instance(CONTEXT_MODULE, $maincm->id); + } else { + return null; + } + + $fs = get_file_storage(); + $filepath = is_null($filepath) ? '/' : $filepath; + $filename = is_null($filename) ? '.' : $filename; + if (!($storedfile = $fs->get_file($filecontext->id, 'mod_glossary', $filearea, $itemid, $filepath, $filename))) { + return null; } + $urlbase = $CFG->wwwroot.'/pluginfile.php'; - return null; + return new file_info_stored($browser, $filecontext, $storedfile, $urlbase, s($entry->concept), true, true, false, false); } /** diff --git a/mod/glossary/locallib.php b/mod/glossary/locallib.php index 5dbe303810d6f..9e247065a4494 100644 --- a/mod/glossary/locallib.php +++ b/mod/glossary/locallib.php @@ -447,3 +447,128 @@ public static function entry_content($course, $cm, $glossary, $entry, $aliases, } } + +/** + * Class representing the virtual node with all itemids in the file browser + * + * @category files + * @copyright 2012 David Mudrak + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class glossary_file_info_container extends file_info { + /** @var file_browser */ + protected $browser; + /** @var stdClass */ + protected $course; + /** @var stdClass */ + protected $cm; + /** @var string */ + protected $component; + /** @var stdClass */ + protected $context; + /** @var array */ + protected $areas; + /** @var string */ + protected $filearea; + + /** + * Constructor (in case you did not realize it ;-) + * + * @param file_browser $browser + * @param stdClass $course + * @param stdClass $cm + * @param stdClass $context + * @param array $areas + * @param string $filearea + */ + public function __construct($browser, $course, $cm, $context, $areas, $filearea) { + parent::__construct($browser, $context); + $this->browser = $browser; + $this->course = $course; + $this->cm = $cm; + $this->component = 'mod_glossary'; + $this->context = $context; + $this->areas = $areas; + $this->filearea = $filearea; + } + + /** + * @return array with keys contextid, filearea, itemid, filepath and filename + */ + public function get_params() { + return array( + 'contextid' => $this->context->id, + 'component' => $this->component, + 'filearea' => $this->filearea, + 'itemid' => null, + 'filepath' => null, + 'filename' => null, + ); + } + + /** + * Can new files or directories be added via the file browser + * + * @return bool + */ + public function is_writable() { + return false; + } + + /** + * Should this node be considered as a folder in the file browser + * + * @return bool + */ + public function is_directory() { + return true; + } + + /** + * Returns localised visible name of this node + * + * @return string + */ + public function get_visible_name() { + return $this->areas[$this->filearea]; + } + + /** + * Returns list of children nodes + * + * @return array of file_info instances + */ + public function get_children() { + global $DB; + + $sql = "SELECT DISTINCT f.itemid, ge.concept + FROM {files} f + JOIN {modules} m ON (m.name = 'glossary' AND m.visible = 1) + JOIN {course_modules} cm ON (cm.module = m.id AND cm.id = ?) + JOIN {glossary} g ON g.id = cm.instance + JOIN {glossary_entries} ge ON (ge.glossaryid = g.id AND ge.id = f.itemid) + WHERE f.contextid = ? AND f.component = ? AND f.filearea = ? + ORDER BY ge.concept, f.itemid"; + $params = array($this->context->instanceid, $this->context->id, $this->component, $this->filearea); + + $rs = $DB->get_recordset_sql($sql, $params); + $children = array(); + foreach ($rs as $file) { + if ($child = $this->browser->get_file_info($this->context, 'mod_glossary', $this->filearea, $file->itemid)) { + $children[] = $child; + } + } + $rs->close(); + + return $children; + } + + /** + * Returns parent file_info instance + * + * @return file_info or null for root + */ + public function get_parent() { + return $this->browser->get_file_info($this->context); + } +}