Moodle: Add (file) resources and upload files into it

File resource in Moodle is another course element that can be used for serving files for students. It’s assumed that there will be only one file per resource, but you can upload multiple files and set one of them as default to be downloaded when student click on the resource.

We’ll be adding multiple resources and multiple files in this example. Doing so using pure php coding would be complicated enough to mess things up very easily, so we’ll use Moodle APIs to do some things for us. And it’ll be additional plus as we will be more Moodle version independent as long as API will remain not changed.

As mentioned before: we’ll add multiple resources to a course (to first section of a course). Resources will be named “Files A”, “File B” and so on. To every resource, we will upload multiple files and will decide which file goes where depending on the file name, eg. files starting with “a” will go to a resource named “Files A”.

While using API is making this more readable and easier to grasp, this is still a complicated process and we need to make sure to do everything right. Test this on your development or testing environment.

What we will do step by step:

  1. Get file resource context ID
  2. Get file storage (singleton)
  3. Create file info data
  4. Upload file

And now, while getting a resource context ID, we’ll have to make sure we will choose the right resource name (eg. “Files A”) and that this resource does exist. So, we’ll need a function that will do that for us and create all needed resources.

Then we’ll need to make sure that newly created resources are visible, are available, have created context, resources are placed in a correct course section and so on. We could also check if module is enabled and many other things, but it’ll be good enough for this example.

And after whole operation, we’ll clean course cache so users could see new course elements.

Let’s see get_resources() function

function get_resource($courseid, $resource_name) {
    global $DB, $CFG;
    $sql = "SELECT cm.id as cmid FROM {course_modules} cm, {resource} res
        WHERE res.name = '" . $resource_name . "'
        AND cm.course = " . $courseid . "
        AND cm.instance = res.id";
        

    if (! $coursemodule = $DB->get_record_sql($sql)) {

        $letters = range('A', 'Z');
        
        require_once($CFG->dirroot.'/course/lib.php');

        echo "\tCreate new resouces\n";

        foreach($letters as $key => $letter) {
            
            $course = $DB->get_record('course', array('id' => $courseid), '*', MUST_EXIST);
            // get module id
            $module = $DB->get_record('modules', array('name' => 'resource'), '*', MUST_EXIST);
            // get course section
            $cw = get_course_section(0, $course->id);
            $sectionid = $DB->get_record('course_sections', array('course' => $this->courseid, 'section' => $cw->id), '*', MUST_EXIST);
            
            $resource_data = new stdClass();
            $resource_data->course = $course->id;
            $resource_data->name = 'Files ' . strtoupper($letter);
            $resource_data->intro = '<p>'.'Files ' . strtoupper($letter).'</p>';
            $resource_data->introformat = 1;
            $resource_data->tobemigrated = 0;
            $resource_data->legacyfiles = 0;
            $resource_data->display = 0;
            $resource_data->displayoptions = 'a:2:{s:12:"printheading";i:0;s:10:"printintro";i:1;}';
            $resource_data->revision = 1;
            $resource_data->completion = 1;
            $resource_data->showavailability = 1;
            $resource_data->timemodified = time();

            $resource_id = $DB->insert_record('resource', $resource_data);

            // add course module
            $cm = new stdClass();
            $cm->course = $courseid;
            $cm->module = $module->id; // should be retrieved from mdl_modules
            $cm->instance = $resource_id; // from mdl_resource
            $cm->section = $sectionid->id; // from mdl_course_sections
            $cm->visible = 1;
            $cm->visibleold = 1;
            $cm->showavailability = 1;
            $cm->added = time();

            $cmid = $DB->insert_record('course_modules', $cm);

            // add module to course section so it'll be visible
            if ($DB->record_exists('course_sections', array('course' => $courseid, 'section' => 1))) {
                $sectionid = $DB->get_record('course_sections', array('course' => $courseid, 'section' => 1));

                // if sequence is not empty, add another course_module id
                if (!empty($sectionid->sequence)) {
                    $sequence = $sectionid->sequence . ',' . $cmid;
                } else {
                    // if sequence is empty, add course_module id
                    $sequence = $cmid;
                }

                $course_section = new stdClass();
                $course_section->id = $sectionid->id;
                $course_section->course = $courseid;
                $course_section->section = 1;
                $course_section->sequence = $sequence;
                $csid = $DB->update_record('course_sections', $course_section);

            } else {
                $sequence = $cmid;

                $course_section = new stdClass();
                $course_section->course = $courseid;
                $course_section->section = 1;
                $course_section->sequence = $sequence;

                $csid = $DB->insert_record('course_sections', $course_section);

            }

            // force clear module cache
            $modulecache = new stdClass();
            $modulecache->id = $courseid;
            $modulecache->sectioncache = 'NULL';
            $DB->update_record('course', $modulecache);
           
        } // foreach

        // get context again, this time with all resources present
        $context = get_resource($courseid, $resource_name);
        return $context;

    } else {
        
        $context = get_context_instance(CONTEXT_MODULE, $coursemodule->cmid);

        return $context;
    }


} // get_resource

Now when we have most difficult part of the job, let’s do something easy. We’ll use a function that will return a first letter of a string and function to clean file name out of strange characters plus, will convert it to pure ASCII (just to be safe).

function clean_string($str) {

    setlocale(LC_ALL, 'en_US');

    $chars = "\\/`!@#$%^&*()_-=+|}{]['\";:?><,. \t\n";

    $str = strtolower( trim( strip_tags($str), $chars) );
    $str = iconv('UTF-8', 'ASCII//IGNORE', $str);

    return $str;
}

function get_first_letter($str) {
    $str = clean_string($str);
    
    // return first letter
    return strtoupper(substr($str, 0, 1));
}

Now, a function that will actually upload something:

function upload_for_course($courseid, $source_filename, $destination) {
    global $DB, $CFG;

    if (file_exists($source_filename) && is_readable($source_filename)) {

        require_once($CFG->dirroot.'/lib/filestorage/file_storage.php');

        $sql = "SELECT m.id moduleid, cm.id cmid, cm.course courseid, cm.module moduleid
            FROM {course_modules} cm, {modules} m
            WHERE m.id = cm.module AND cm.course=$courseid AND m.name='resource'";

        $resource = get_resource($courseid, $destination['resource_name']);

        $fs = get_file_storage();

        // Prepare file record object
        $fileinfo = array(
            'contextid' => $resource->id, // ID of context
            'component' => 'mod_resource',     // usually = table name
            'filearea' => 'content',     // usually = table name
            'itemid' => 0,               // usually = ID of row in table
            'filepath' => $destination['path'],           // any path beginning and ending in /
            'filename' => $destination['filename']); // any filename


        $fs->create_file_from_pathname($fileinfo, $source_filename);


    } else {
        die("Error: Not exist or not readable: " . $source_filename . "\n");
    }

}

Is that it? Almost done now. Bare with me.

This is the main script you’ll want to run to upload a file. You may use more logic to it or run it in the loop to upload multiple files (bash script example below).

define('CLI_SCRIPT', true);

// this is config.php within Moodle installation
require_once('/path/to/moodle/config.php');

$courseid = $argv[1];
$filename = $argv[2];

$course_name = $DB->get_record('course', array('id' => $courseid), 'id, shortname')
        or die("Course not found");

$course_destination['path'] = "/files/"; // any path that starts and ends with "/"
$course_destination['filename'] = $filename;
$course_destination['resource_name'] = 'Files ' . get_first_letter(clean_string($filename));

if (file_exists($filename)) {
    upload_for_course($uploadcourseid, $path, $course_destination);
} else {
    echo "Not uploading, file does not exist\n";
}

You can save above script as uploader.php and run it from command line:

php uploader.php 222 "/some/file/name.pdf"

Where 222 is course ID and “/some/file/name.pdf” is a full path to a file you want to upload. Easy peasy.

A bash script that can enable you to upload all files in a directory

#!/bin/bash

ID=$1
DIR=$2

PHP=`which php`

for f in `find $DIR -type f -print -maxdepth 2`;
do
    echo "Processing $line"
    $PHP ./uploader.php $ID "$f"

done

And again, you can run it by executing:

./multiupload.sh 222 /some/dir

Some warnings:

  • This may mess up your course to a point you’ll have to delete it or even reinstall Moodle. Please backup Moodle DB and Moodle data directory before you run it
  • To run this script, you have to have the right permissions – eg. writing permission to Moodle data directory. You can bypass that by running it with su command or as root (not recommended)

I won’t provide you with the ready script package intentionally. You really need to know what are you doing before you run it. Please test it on your test environment and before you execute that on your live site, please make sure to make a fresh backup and set Moodle site to the maintenance mode.

Let me know if that example helped you with something and help me correct any errors you may spot in above scripts. Thank you.

Related Posts

Moodle: Adding label to a course using code Adding lablel to any course is a very simple operation. Label is the most basic element you can add to a course body in Moodle (next to course section...
Integrating Oracle backups with Bacula In the last post I've described how to create simple RMAN backups executed using simple scripts. I've done that to introduce you to integrating these ...
Moodle: modify form elements before displaying the... Moodle forms are built on top of PEAR QuickForm lib with some non standard js code and other extensions and modifications. It's dead simple to generat...

Leave a Reply

Your email address will not be published. Required fields are marked *

Loading Facebook Comments ...

No Trackbacks.