Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Assign submission plugins: Difference between revisions

From MoodleDocs
Line 17: Line 17:
Note: The plugin name should be no longer than 11 characters - this is because the database tables for a submission plugin must be prefixed with "assignsubmission_" + pluginname (17 chars + X) and the table names can be no longer than 28 chars (thanks oracle). If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 28 character limit.
Note: The plugin name should be no longer than 11 characters - this is because the database tables for a submission plugin must be prefixed with "assignsubmission_" + pluginname (17 chars + X) and the table names can be no longer than 28 chars (thanks oracle). If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 28 character limit.


version.php
All examples in this document exclude the required copyright and license information from source files for brevity.
 
=== version.php ===


To start with we need to tell Moodle the version information for our new plugin so that it can be installed and upgraded correctly. This information is added to version.php as with any other type of Moodle plugin. The component name must begin with "submission_" to identify this as a submission plugin.
To start with we need to tell Moodle the version information for our new plugin so that it can be installed and upgraded correctly. This information is added to version.php as with any other type of Moodle plugin. The component name must begin with "submission_" to identify this as a submission plugin.
Line 23: Line 25:
See [[version.php]] for more information.
See [[version.php]] for more information.
<pre>
<pre>
$plugin->version  = 2012011600;
defined('MOODLE_INTERNAL') || die();                                                                                               
$plugin->requires  = 2011110200;
                                                                                                                                   
$plugin->component = 'submission_file';
$plugin->version  = 2012112900;                                                                                                  
$plugin->requires  = 2012112900;                                                                                                  
$plugin->component = 'assignsubmission_file';
</pre>


</pre>
=== settings.php ===
 
The settings file allows us to add custom settings to the system wide configuration page for our plugin.
 
All submission settings should be named 'assignsubmission_pluginname/settingname' in order for the setting to be associated with the plugin.


settings.php
All submission plugins should include one setting named 'default' to indicate if the plugin should be enabled by default when creating a new assignment.  


The settings file allows us to add custom settings to the system wide configuration page for our plugin. The file plugin checks to see if there is a maxbytes setting for this moodle installation and if found, it adds a new admin setting to the settings page. The name of the setting should begin with the plugin component name ("submission_file") in this case. The strings are specified in this plugins language file.  
This example from the submission_file plugin also checks to see if there is a maxbytes setting for this moodle installation and if found, it adds a new admin setting to the settings page. The name of the setting should begin with the plugin component name ("assignsubmission_file") in this case. The strings are specified in this plugins language file.  


<pre>
<pre>
if (isset($CFG->maxbytes)) {
// Note: This is on by default.                                                                                                   
     $settings->add(new admin_setting_configselect('submission_file_maxbytes',  
$settings->add(new admin_setting_configcheckbox('assignsubmission_file/default',                                                   
                        get_string('maximumsubmissionsize', 'submission_file'),
                  new lang_string('default', 'assignsubmission_file'),                                                           
                        get_string('configmaxbytes', 'submission_file'), 1048576,      
                  new lang_string('default_help', 'assignsubmission_file'), 1));                                                 
                        get_max_upload_sizes($CFG->maxbytes)));
                                                                                                                                   
}
if (isset($CFG->maxbytes)) {                                                                                                      
                                                                                                                                   
     $name = new lang_string('maximumsubmissionsize', 'assignsubmission_file');                                                     
    $description = new lang_string('configmaxbytes', 'assignsubmission_file');                                                     
                                                                                                                                   
    $element = new admin_setting_configselect('assignsubmission_file/maxbytes',                                                    
                                              $name,                                                                               
                                              $description,                                                                        
                                              1048576,                                                                            
                                              get_max_upload_sizes($CFG->maxbytes));                                               
    $settings->add($element);                                                                                                      
}                                                                        
</pre>
</pre>


lang/en/submission_file.php
=== lang/en/submission_pluginname.php ===


The language file for this plugin must have the same name component name ("submission_file.php"). It should at least define a string for "pluginname".
The language file for this plugin must have the same name as the component name (e.g. "submission_file.php"). It should at least define a string for "pluginname". For example:


<pre>
<pre>
$string['allowfilesubmissions'] = 'Enabled';
$string['pluginname'] = 'Awesome submissions';    
$string['configmaxbytes'] = 'Maximum file size';
$string['file'] = 'File Submissions';
$string['maxbytes'] = 'Maximum file size';
$string['maxfilessubmission'] = 'Maximum number of uploaded files';
$string['maximumsubmissionsize'] = 'Maximum submission size';
$string['pluginname'] = 'File Submissions';
$string['submissionfilearea'] = 'Uploaded submission files';
</pre>
</pre>


db/access.php
=== db/access.php ===


This is where any additional capabilities are defined (None for this plugin).
This is where any additional capabilities are defined if required. This file can be omitted if there are no capabilities added by the plugin.


See [[Activity_modules#access.php]] for more information.
<pre>
<pre>
$capabilities = array(
$capabilities = array(
 
    'submissionplugin/dungeon:master' => array(
        'riskbitmask' => RISK_XSS,
        'captype' => 'write',
        'contextlevel' => CONTEXT_COURSE,
        'archetypes' => array(
            'editingteacher' => CAP_ALLOW,
            'manager' => CAP_ALLOW
        ),
        'clonepermissionsfrom' => 'moodle/course:manageactivities'
    ),
);
);


</pre>
</pre>


db/upgrade.php
=== db/upgrade.php ===


This is where any upgrade code is defined (None for this plugin).  
This is where any upgrade code is defined.  


See [[Activity_modules#upgrade.php]] for more infomation.
<pre>
<pre>
function xmldb_submission_file_upgrade($oldversion) {
function xmldb_submission_file_upgrade($oldversion) {
Line 77: Line 101:


     $dbman = $DB->get_manager();
     $dbman = $DB->get_manager();
    if ($oldversion < 2012091800) {
        // Put upgrade code here
        // Savepoint reached.
        upgrade_plugin_savepoint(true, 2012091800, 'assignsubmission', 'file');
    }


    // do the upgrades
     return true;
     return true;
}
}
Line 84: Line 113:
</pre>
</pre>


db/install.xml
=== db/install.xml ===


This is where any database tables required to save this plugins data are defined. In this case we define a table that links to submission and contains a column to record the number of files.  
This is where any database tables required to save this plugins data are defined. File submissions define a table that links to submission and contains a column to record the number of files.  


<pre>
<pre>
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8" ?>                                                                                            
<XMLDB PATH="mod/assign/submission/file/db" VERSION="20090420" COMMENT="XMLDB file for Moodle mod/assign/submission/file"
<XMLDB PATH="mod/assign/submission/file/db" VERSION="20120423" COMMENT="XMLDB file for Moodle mod/assign/submission/file"          
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                                                          
     xsi:noNamespaceSchemaLocation="../../../lib/xmldb/xmldb.xsd"
     xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"                                                            
>
>                                                                                                                                  
   <TABLES>
   <TABLES>                                                                                                                        
     <TABLE NAME="assign_submission_file" COMMENT="Info about submitted assignments">
     <TABLE NAME="assignsubmission_file" COMMENT="Info about file submissions for assignments">                                    
       <FIELDS>
       <FIELDS>                                                                                                                    
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="assignment"/>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>                                                  
         <FIELD NAME="assignment" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="submission"/>
         <FIELD NAME="assignment" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>                              
         <FIELD NAME="submission" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="assignment" NEXT="numfiles"/>
         <FIELD NAME="submission" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>                              
         <FIELD NAME="numfiles" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="submission" />
         <FIELD NAME="numfiles" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The number of files the student submitted."/>
       </FIELDS>
       </FIELDS>                                                                                                                    
       <KEYS>
       <KEYS>                                                                                                                      
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="assignment"/>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this submission info."/>                        
         <KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" PREVIOUS="primary" NEXT="submission"/>
         <KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" COMMENT="The assignment instance this submission relates to"/>
         <KEY NAME="submission" TYPE="foreign" FIELDS="submission" REFTABLE="assign_submission" REFFIELDS="id" PREVIOUS="assignment"/>
         <KEY NAME="submission" TYPE="foreign" FIELDS="submission" REFTABLE="assign_submission" REFFIELDS="id" COMMENT="The submission this file submission relates to."/>
       </KEYS>
       </KEYS>                                                                                                                      
     </TABLE>
     </TABLE>                                                                                                                      
   </TABLES>
   </TABLES>                                                                                                                        
</XMLDB>
</XMLDB>                          
 
</pre>
</pre>


db/install.php
=== db/install.php ===


This example is actually from the submission_comments plugin - not the submission_file plugin - but it shows how to run custom code on installation of the plugin. In this case it makes the comments plugin the last of the three submission plugins installed by default.  
This example is from the submission_comments plugin. It shows how to run custom code on installation of the plugin. In this case it makes the comments plugin the last of the three submission plugins installed by default.  


<pre>
<pre>
Line 135: Line 163:
     }
     }
          
          
    // do the upgrades
     return true;
     return true;
}
}
Line 141: Line 168:
</pre>
</pre>


lib.php
=== locallib.php ===


This is where all the functionality for this plugin is defined. We will step through this file and describe each part as we go.
This is where all the functionality for this plugin is defined. We will step through this file and describe each part as we go.
Line 147: Line 174:
<pre>
<pre>


class submission_file extends submission_plugin {
class assign_submission_file extends assign_submission_plugin {


</pre>
</pre>


All submission plugins MUST define a class with the component name of the plugin that extends submission plugin.  
All submission plugins MUST define a class with the component name of the plugin that extends assign_submission_plugin.  


<pre>
<pre>


     public function get_name() {
     public function get_name() {
         return get_string('file', 'submission_file');
         return get_string('file', 'assignsubmission_file');
     }
     }


Line 164: Line 191:


<pre>
<pre>
     public function get_settings(&$mform) {
     public function get_settings(MoodleQuickForm $mform) {
        global $CFG, $COURSE, $DB;
      global $CFG, $COURSE;                                                                                                      
 
                                                                                                                                   
         $default_maxfilesubmissions = $this->get_config('maxfilesubmissions');
         $defaultmaxfilesubmissions = $this->get_config('maxfilesubmissions');                                                      
         $default_maxsubmissionsizebytes = $this->get_config('maxsubmissionsizebytes');
         $defaultmaxsubmissionsizebytes = $this->get_config('maxsubmissionsizebytes');                                              
 
                                                                                                                                   
         $settings = array();
         $settings = array();                                                                                                      
         $options = array();
         $options = array();                                                                                                        
         for($i = 1; $i <= ASSIGN_MAX_SUBMISSION_FILES; $i++) {
         for ($i = 1; $i <= ASSIGNSUBMISSION_FILE_MAXFILES; $i++) {                                                                
             $options[$i] = $i;
             $options[$i] = $i;                                                                                                    
         }
         }                                                                                                                          
 
                                                                                                                                   
         $mform->addElement('select', 'submission_file_maxfiles', get_string('maxfilessubmission', 'submission_file'), $options);
        $name = get_string('maxfilessubmission', 'assignsubmission_file');                                                         
         $mform->setDefault('submission_file_maxfiles', $default_maxfilesubmissions);
         $mform->addElement('select', 'assignsubmission_file_maxfiles', $name, $options);                                           
 
        $mform->addHelpButton('assignsubmission_file_maxfiles',                                                                   
         $choices = get_max_upload_sizes($CFG->maxbytes, $COURSE->maxbytes);
                              'maxfilessubmission',                                                                                
        $choices[0] = get_string('courseuploadlimit') . ' ('.display_size($COURSE->maxbytes).')';
                              'assignsubmission_file');                                                                           
         $settings[] = array('type' => 'select',
        $mform->setDefault('assignsubmission_file_maxfiles', $defaultmaxfilesubmissions);                                          
                             'name' => 'maxsubmissionsizebytes',
         $mform->disabledIf('assignsubmission_file_maxfiles', 'assignsubmission_file_enabled', 'notchecked');                      
                             'description' => get_string('maximumsubmissionsize', 'submission_file'),
                                                                                                                                   
                             'options'=>$choices,
         $choices = get_max_upload_sizes($CFG->maxbytes,                                                                            
                             'default'=>$default_maxsubmissionsizebytes);
                                        $COURSE->maxbytes,                                                                         
 
                                        get_config('assignsubmission_file', 'maxbytes'));                                          
         $mform->addElement('select', 'submission_file_maxsizebytes', get_string('maximumsubmissionsize', 'submission_file'), $choices);
                                                                                                                                   
         $mform->setDefault('submission_file_maxsizebytes', $default_maxsubmissionsizebytes);
         $settings[] = array('type' => 'select',                                                                                    
 
                             'name' => 'maxsubmissionsizebytes',                                                                    
 
                             'description' => get_string('maximumsubmissionsize', 'assignsubmission_file'),                        
                             'options'=> $choices,                                                                                  
                             'default'=> $defaultmaxsubmissionsizebytes);                                                          
                                                                                                                                   
        $name = get_string('maximumsubmissionsize', 'assignsubmission_file');                                                     
         $mform->addElement('select', 'assignsubmission_file_maxsizebytes', $name, $choices);                                       
        $mform->addHelpButton('assignsubmission_file_maxsizebytes',                                                               
                              'maximumsubmissionsize',                                                                            
                              'assignsubmission_file');                                                                            
         $mform->setDefault('assignsubmission_file_maxsizebytes', $defaultmaxsubmissionsizebytes);                                  
        $mform->disabledIf('assignsubmission_file_maxsizebytes',                                                                   
                          'assignsubmission_file_enabled',                                                                       
                          'notchecked');                           
     }
     }
</pre>
</pre>
Line 197: Line 236:


<pre>
<pre>
     public function save_settings($mform) {
     public function save_settings(stdClass $data) {                                                                                
         $this->set_config('maxfilesubmissions', $mform->submission_file_maxfiles);
         $this->set_config('maxfilesubmissions', $data->assignsubmission_file_maxfiles);                                            
         $this->set_config('maxsubmissionsizebytes', $mform->submission_file_maxsizebytes);
         $this->set_config('maxsubmissionsizebytes', $data->assignsubmission_file_maxsizebytes);                                    
         return true;
         return true;                                                                                                              
     }
     }  
</pre>
</pre>


The "save_settings" function is called when the assignment settings page is submitted, either for a new assignment or when editing an existing one. For settings specific to a single instance of the assignment you can use the assignment_plugin::set_config function shown here to save key/value pairs against this assignment instance for this plugin.  
The "save_settings" function is called when the assignment settings page is submitted, either for a new assignment or when editing an existing one. For settings specific to a single instance of the assignment you can use the assign_plugin::set_config function shown here to save key/value pairs against this assignment instance for this plugin.  


<pre>
<pre>
     public function get_form_elements($submission, & $mform, & $data) {
     public function get_form_elements($submission, MoodleQuickForm $mform, stdClass $data) {                                      
 
                                                                                                                                   
        $elements = array();
         if ($this->get_config('maxfilesubmissions') <= 0) {                                                                        
 
             return false;                                                                                                          
         if ($this->get_config('maxfilesubmissions') <= 0) {
         }                                                                                                                          
             return $elements;
                                                                                                                                   
         }
         $fileoptions = $this->get_file_options();                                                                                  
 
         $submissionid = $submission ? $submission->id : 0;                                                                        
         $fileoptions = $this->get_file_options();
                                                                                                                                   
         $submissionid = $submission ? $submission->id : 0;
         $data = file_prepare_standard_filemanager($data,                                                                          
 
                                                  'files',                                                                        
 
                                                  $fileoptions,                                                                    
         $data = file_prepare_standard_filemanager($data, 'files', $fileoptions, $this->assignment->get_context(), 'mod_assign', ASSIGN_FILEAREA_SUBMISSION_FILES, $submissionid);
                                                  $this->assignment->get_context(),                                                
         $mform->addElement('filemanager', 'files_filemanager', '', null, $fileoptions);
                                                  'assignsubmission_file',                                                        
         return true;
                                                  ASSIGNSUBMISSION_FILE_FILEAREA,                                                  
     }
                                                  $submissionid);                                                                  
         $mform->addElement('filemanager', 'files_filemanager', html_writer::tag('span', $this->get_name(),                         
            array('class' => 'accesshide')), null, $fileoptions);                                                                  
                                                                                                                                   
         return true;                                                                                                              
     }                                  


</pre>
</pre>


The get_form_elements function is called when building the submission form. It functions identically to the get_settings function except that the submission object is available (if there is a submission) to associate the settings with a single submission. This function also shows how to use a filemanager within a submission plugin. The function must return true if it has modified the form otherwise the assignment will not include a header for this plugin.  
The get_form_elements function is called when building the submission form. It functions identically to the get_settings function except that the submission object is available (if there is a submission) to associate the settings with a single submission. This example also shows how to use a filemanager within a submission plugin. The function must return true if it has modified the form otherwise the assignment will not include a header for this plugin.  


<pre>
<pre>
    public function save($submission, $data) {
  public function save(stdClass $submission, stdClass $data) {                                                                  
 
         global $USER, $DB;                                                                                                        
         global $USER, $DB;
                                                                                                                                   
 
         $fileoptions = $this->get_file_options();                                                                                  
         $fileoptions = $this->get_file_options();
                                                                                                                                   
 
         $data = file_postupdate_standard_filemanager($data,                                                                        
 
                                                    'files',                                                                      
         $data = file_postupdate_standard_filemanager($data, 'files', $fileoptions, $this->assignment->get_context(), 'mod_assign', ASSIGN_FILEAREA_SUBMISSION_FILES, $submission->id);
                                                    $fileoptions,                                                                
 
                                                    $this->assignment->get_context(),                                            
 
                                                    'assignsubmission_file',                                                      
         $file_submission = $this->get_file_submission($submission->id);
                                                    ASSIGNSUBMISSION_FILE_FILEAREA,                                              
 
                                                    $submission->id);                                                            
         //plagiarism code event trigger when files are uploaded
                                                                                                                                   
 
         $filesubmission = $this->get_file_submission($submission->id);                                                            
         $fs = get_file_storage();
                                                                                                                                   
         $files = $fs->get_area_files($this->assignment->get_context()->id, 'mod_assign', ASSIGN_FILEAREA_SUBMISSION_FILES, $submission->id, "id", false);
         // Plagiarism code event trigger when files are uploaded.                                                                 
 
                                                                                                                                   
         // send files to event system
         $fs = get_file_storage();                                                                                                  
         // Let Moodle know that an assessable file was uploaded (eg for plagiarism detection)
         $files = $fs->get_area_files($this->assignment->get_context()->id,                                                        
         $eventdata = new stdClass();
                                    'assignsubmission_file',                                                                      
         $eventdata->modulename = 'assign';
                                    ASSIGNSUBMISSION_FILE_FILEAREA,                                                              
         $eventdata->cmid = $this->assignment->get_course_module()->id;
                                    $submission->id,                                                                              
         $eventdata->itemid = $submission->id;
                                    'id',                                                                                        
         $eventdata->courseid = $this->assignment->get_course()->id;
                                    false);                                                                                      
         $eventdata->userid = $USER->id;
                                                                                                                                   
         if ($files) {
        $count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);                                             
             $eventdata->files = $files;
                                                                                                                                   
         }
         // Send files to event system.                                                                                             
         events_trigger('assessable_file_uploaded', $eventdata);
         // This lets Moodle know that an assessable file was uploaded (eg for plagiarism detection).                               
 
         $eventdata = new stdClass();                                                                                              
 
         $eventdata->modulename = 'assign';                                                                                        
         if ($file_submission) {
         $eventdata->cmid = $this->assignment->get_course_module()->id;                                                            
             $file_submission->numfiles = $this->count_files($submission->id);
         $eventdata->itemid = $submission->id;                                                                                      
             return $DB->update_record('assign_submission_file', $file_submission);
         $eventdata->courseid = $this->assignment->get_course()->id;                                                                
         } else {
         $eventdata->userid = $USER->id;                                                                                            
             $file_submission = new stdClass();
         if ($count > 1) {                                                                                                          
             $file_submission->numfiles = $this->count_files($submission->id);
             $eventdata->files = $files;                                                                                            
             $file_submission->submission = $submission->id;
         }                                                                                                                          
             $file_submission->assignment = $this->assignment->get_instance()->id;
        $eventdata->file = $files;                                   
             return $DB->insert_record('assign_submission_file', $file_submission) > 0;
        $eventdata->pathnamehashes = array_keys($files);                                                                           
         }
         events_trigger('assessable_file_uploaded', $eventdata);                                                                    
    }
                                                                                                                                   
 
         if ($filesubmission) {                                                                                                    
             $filesubmission->numfiles = $this->count_files($submission->id,                                                       
                                                          ASSIGNSUBMISSION_FILE_FILEAREA);                                        
             return $DB->update_record('assignsubmission_file', $filesubmission);                                                  
         } else {                                                                                                                  
             $filesubmission = new stdClass();                                                                                      
             $filesubmission->numfiles = $this->count_files($submission->id,                                                       
                                                          ASSIGNSUBMISSION_FILE_FILEAREA);                                        
             $filesubmission->submission = $submission->id;                                                                        
             $filesubmission->assignment = $this->assignment->get_instance()->id;                                                  
             return $DB->insert_record('assignsubmission_file', $filesubmission) > 0;                                              
         }                              
</pre>
</pre>


The "save" function is called to save a user submission. The parameters are the submission object and the data from the submission form. This example calls file_postupdate_standard_filemanager to copy the files from the draft file area to the filearea for this submission, it then uses the event api to trigger an assessable_file_uploaded event for the plagiarism api. It then records the number of files in the plugin specific "assign_submission_file" table.  
The "save" function is called to save a user submission. The parameters are the submission object and the data from the submission form. This example calls file_postupdate_standard_filemanager to copy the files from the draft file area to the filearea for this submission, it then uses the event api to trigger an assessable_file_uploaded event for the plagiarism api. It then records the number of files in the plugin specific "assignsubmission_file" table.  


<pre>
<pre>
     public function get_files($submission) {
     public function get_files($submission) {
        global $DB;
         $result = array();                                                                                                        
         $result = array();
         $fs = get_file_storage();                                                                                                  
         $fs = get_file_storage();
                                                                                                                                   
 
         $files = $fs->get_area_files($this->assignment->get_context()->id,                                                        
         $files = $fs->get_area_files($this->assignment->get_context()->id, 'mod_assign', ASSIGN_FILEAREA_SUBMISSION_FILES, $submission->id, "timemodified", false);
                                    'assignsubmission_file',                                                                      
 
                                    ASSIGNSUBMISSION_FILE_FILEAREA,                                                              
         foreach ($files as $file) {
                                    $submission->id,                                                                              
             $result[$file->get_filename()] = $file;
                                    'timemodified',                                                                              
         }
                                    false);                                                                                      
                                                                                                                                   
         foreach ($files as $file) {                                                                                                
             $result[$file->get_filename()] = $file;                                                                                
         }                                                                                                                          
         return $result;
         return $result;
     }
     }
Line 292: Line 351:
</pre>
</pre>


If this submission plugin produces one or more files, it should implement "get_files" so that the portfolio API can export a list of all the files from all of the plugins for this assignment submission.  
If this submission plugin produces one or more files, it should implement "get_files" so that the portfolio API can export a list of all the files from all of the plugins for this assignment submission. This is also used by the offline grading feature in the assignment.


<pre>
<pre>
     public function view_summary($submission) {
     public function view_summary(stdClass $submission, & $showviewlink) {                                                          
         $count = $this->count_files($submission->id);
         $count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);                                              
         if ($count <= ASSIGN_SUBMISSION_FILE_MAX_SUMMARY_FILES) {
                                                                                                                                   
             return $this->assignment->render_area_files(ASSIGN_FILEAREA_SUBMISSION_FILES, $submission->id);
        // Show we show a link to view all files for this plugin?                                                                 
         } else {
        $showviewlink = $count > ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES;                                                           
             return get_string('countfiles', 'submission_file', $count);
         if ($count <= ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES) {                                                                    
         }
             return $this->assignment->render_area_files('assignsubmission_file',                                                  
                                                        ASSIGNSUBMISSION_FILE_FILEAREA,                                           
                                                        $submission->id);                                                          
         } else {                                                                                                                  
             return get_string('countfiles', 'assignsubmission_file', $count);                                                      
         }                                                                                                                          
     }
     }
</pre>
</pre>


The view_summary function is called to display a summary of the submission to both markers and students. It counts the numbre of files submitted and if it is more that a set number (5) it only displays a count of how many files have been updated - otherwise it uses a helper function to write the entire list of files. This is because we want to keep the summaries really short so they can be displayed in a table. There will be a link to view the full submission on the submission status page.  
The view_summary function is called to display a summary of the submission to both markers and students. It counts the number of files submitted and if it is more that a set number, it only displays a count of how many files are in the submission - otherwise it uses a helper function to write the entire list of files. This is because we want to keep the summaries really short so they can be displayed in a table. There will be a link to view the full submission on the submission status page.  


<pre>
<pre>
     public function view($submission) {
     public function view($submission) {
         return $this->assignment->render_area_files(ASSIGN_FILEAREA_SUBMISSION_FILES, $submission->id);
         return $this->assignment->render_area_files('assignsubmission_file',                                                      
                                                    ASSIGNSUBMISSION_FILE_FILEAREA,                                               
                                                    $submission->id);
     }
     }
</pre>
</pre>


The view function is called to display the entire submission to both markers and students. In this case it uses the helper function in the assignment class to write the list of files.  
The view function is called to display the entire submission to both markers and students. In this case it uses the helper function in the assignment class to write the list of files.  
<pre>
    public function show_view_link($submission) {
        $count = $this->count_files($submission->id);
        return $count > ASSIGN_SUBMISSION_FILE_MAX_SUMMARY_FILES;
    }
</pre>
The show_view_link function allows the plugin to control whether the assignment module renders a link to show the full submission or not. This is because some submission types can display the entire submission in the summary and some cannot.


<pre>
<pre>
Line 338: Line 395:
</pre>
</pre>


The can_upgrade function is used to identify old assignment subtypes that can be upgraded by this plugin. This plugin supports upgrades from the old "upload" and "uploadsingle" assignment subtypes.  
The can_upgrade function is used to identify old "Assignment 2.2" subtypes that can be upgraded by this plugin. This plugin supports upgrades from the old "upload" and "uploadsingle" assignment subtypes.  


<pre>
<pre>
      
      
     public function upgrade_settings($oldassignment, & $log) {
     public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) {                                      
         // TODO: get the maxbytes and maxfiles settings from the old assignment and set them for this plugin
        global $DB;                                                                                                               
        return true;
                                                                                                                                   
     }
         if ($oldassignment->assignmenttype == 'uploadsingle') {                                                                   
            $this->set_config('maxfilesubmissions', 1);                                                                           
            $this->set_config('maxsubmissionsizebytes', $oldassignment->maxbytes);                                                 
            return true;                                                                                                           
        } else if ($oldassignment->assignmenttype == 'upload') {                                                                   
            $this->set_config('maxfilesubmissions', $oldassignment->var1);                                                         
            $this->set_config('maxsubmissionsizebytes', $oldassignment->maxbytes);                                                 
                                                                                                                                   
            // Advanced file upload uses a different setting to do the same thing.                                                 
            $DB->set_field('assign',                                                                                               
                          'submissiondrafts',                                                                                     
                          $oldassignment->var4,                                                                                   
                          array('id'=>$this->assignment->get_instance()->id));                                                   
                                                                                                                                   
            // Convert advanced file upload "hide description before due date" setting.                                           
            $alwaysshow = 0;                                                                                                       
            if (!$oldassignment->var3) {                                                                                           
                $alwaysshow = 1;                                                                                                   
            }                                                                                                                     
            $DB->set_field('assign',                                                                                               
                          'alwaysshowdescription',                                                                               
                          $alwaysshow,                                                                                           
                          array('id'=>$this->assignment->get_instance()->id));                                                   
            return true;                                                                                                          
        }                                                                                                                         
     }                    


</pre>
</pre>


This function is called once per assignment instance to upgrade the settings from the old assignment to the new mod_assign. In this case it should be setting the maxbytes and maxfiles configuration settings (but it isn't yet!).  
This function is called once per assignment instance to upgrade the settings from the old assignment to the new mod_assign. In this case it sets the maxbytes, maxfiles and alwaysshowdescription configuration settings.  


<pre>
<pre>
Line 391: Line 473:
</pre>
</pre>


The "upgrade" function upgrades a single submission from the old assignment type to the new one. In this case it involves copying all the files from the old filearea to the new one. There is a helper function available in the assignment class for this (Note: the copy will be fast as it is just adding rows to the files table).
The "upgrade" function upgrades a single submission from the old assignment type to the new one. In this case it involves copying all the files from the old filearea to the new one. There is a helper function available in the assignment class for this (Note: the copy will be fast as it is just adding rows to the files table). If this function returns false, the upgrade will be aborted and rolled back.


<pre>
<pre>
     public function get_editor_text($name, $submissionid) {
                                                           
         if ($name == 'onlinetext') {
     public function upgrade(context $oldcontext,                                                                                   
             $onlinetext_submission = $this->get_onlinetext_submission($submissionid);
                            stdClass $oldassignment,                                                                               
            if ($onlinetext_submission) {
                            stdClass $oldsubmission,                                                                               
                return $onlinetext_submission->onlinetext;
                            stdClass $submission,                                                                                  
            }
                            & $log) {                                                                                              
        }
        global $DB;                                                                                                               
                                                                                                                                   
         $filesubmission = new stdClass();                                                                                         
                                                                                                                                   
        $filesubmission->numfiles = $oldsubmission->numfiles;                                                                     
        $filesubmission->submission = $submission->id;                                                                             
        $filesubmission->assignment = $this->assignment->get_instance()->id;                                                       
                                                                                                                                   
        if (!$DB->insert_record('assignsubmission_file', $filesubmission) > 0) {                                                  
             $log .= get_string('couldnotconvertsubmission', 'mod_assign', $submission->userid);                                   
            return false;                                                                                                         
        }                                                                                                                         
                                                                                                                                   
        // Now copy the area files.                                                                                               
        $this->assignment->copy_area_files_for_upgrade($oldcontext->id,                                                           
                                                        'mod_assignment',                                                         
                                                        'submission',                                                             
                                                        $oldsubmission->id,                                                       
                                                        $this->assignment->get_context()->id,                                     
                                                        'assignsubmission_file',                                                   
                                                        ASSIGNSUBMISSION_FILE_FILEAREA,                                           
                                                        $submission->id);                                                          
                                                                                                                                   
        return true;                                                                                                               
    }                             
</pre>
 
This example is from assignsubmission_onlinetext.  If the plugin uses a text-editor it is ideal if the plugin implements "get_editor_fields". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. If a plugin supports multiple text areas it can return the name of each of them here.
 
<pre>    public function get_editor_fields() {                                                                                          
        return array('onlinetext' => get_string('pluginname', 'assignsubmission_comments'));                                      
    }      
</pre>
 
This example is from assignsubmission_onlinetext.  If the plugin uses a text-editor it is ideal if the plugin implements "get_editor_text". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. The name is used to distinguish between multiple text areas in the one plugin.


         return '';
<pre>
     }
    public function get_editor_text($name, $submissionid) {                                                                       
        if ($name == 'onlinetext') {                                                                                               
            $onlinetextsubmission = $this->get_onlinetext_submission($submissionid);                                               
            if ($onlinetextsubmission) {                                                                                           
                return $onlinetextsubmission->onlinetext;                                                                         
            }                                                                                                                     
        }                                                                                                                         
                                                                                                                                   
         return '';                                                                                                                
     }            
</pre>
</pre>


This example is from submission_onlinetext - not submission_file. If the plugin uses a text-editor it is ideal if the plugin implements "get_editor_text". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. The name is used to distinguish between multiple text areas in the one plugin.  
This example is from assignsubmission_onlinetext. For the same reason as the previous function, if the plugin uses a text editor, it is ideal if the plugin implements "get_editor_format". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. The name is used to distinguish between multiple text areas in the one plugin.


<pre>
<pre>
    /**
    * get the content format for the editor
    * @param string $name
    * @param int $submissionid
    * @return bool
    */
     public function get_editor_format($name, $submissionid) {
     public function get_editor_format($name, $submissionid) {
         if ($name == 'onlinetext') {
         if ($name == 'onlinetext') {
Line 423: Line 542:
         }
         }


 
        return 0;
        return 0;
     }
     }


</pre>
</pre>
This example is from submission_onlinetext - not submission_file. For the same reason as the previous function, if the plugin uses a text editor, it is ideal if the plugin implements "get_editor_format". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. The name is used to distinguish between multiple text areas in the one plugin.

Revision as of 06:31, 18 April 2013

Introduction

This page gives an overview of assignment submission plugin and then explains to implement a new one to capture a new type of student submission within the assignment module.

Overview of an assignment submission module

An assignment submission module is used to display custom form fields to a student when they are editing their assignment submission. It can has full control over the display the submitted assignment to graders and students. Plugins participate in all assignment features including backup/restore, upgrades from 2.2, offline grading, group assignments and blind marking.


History

Assignment submission modules were added with the assignment module rewrite for Moodle 2.3.

Template

A great example is the "onlinetext" submission plugin included with Moodle core.

File structure

The files for a custom submission plugin sit under "mod/assign/submission/<pluginname>". A plugin should not include any custom files outside of it's own plugin folder.

Note: The plugin name should be no longer than 11 characters - this is because the database tables for a submission plugin must be prefixed with "assignsubmission_" + pluginname (17 chars + X) and the table names can be no longer than 28 chars (thanks oracle). If a plugin requires multiple database tables, the plugin name will need to be shorter to allow different table names to fit under the 28 character limit.

All examples in this document exclude the required copyright and license information from source files for brevity.

version.php

To start with we need to tell Moodle the version information for our new plugin so that it can be installed and upgraded correctly. This information is added to version.php as with any other type of Moodle plugin. The component name must begin with "submission_" to identify this as a submission plugin.

See version.php for more information.

defined('MOODLE_INTERNAL') || die();                                                                                                
                                                                                                                                    
$plugin->version   = 2012112900;                                                                                                    
$plugin->requires  = 2012112900;                                                                                                    
$plugin->component = 'assignsubmission_file';

settings.php

The settings file allows us to add custom settings to the system wide configuration page for our plugin.

All submission settings should be named 'assignsubmission_pluginname/settingname' in order for the setting to be associated with the plugin.

All submission plugins should include one setting named 'default' to indicate if the plugin should be enabled by default when creating a new assignment.

This example from the submission_file plugin also checks to see if there is a maxbytes setting for this moodle installation and if found, it adds a new admin setting to the settings page. The name of the setting should begin with the plugin component name ("assignsubmission_file") in this case. The strings are specified in this plugins language file.

// Note: This is on by default.                                                                                                     
$settings->add(new admin_setting_configcheckbox('assignsubmission_file/default',                                                    
                   new lang_string('default', 'assignsubmission_file'),                                                             
                   new lang_string('default_help', 'assignsubmission_file'), 1));                                                   
                                                                                                                                    
if (isset($CFG->maxbytes)) {                                                                                                        
                                                                                                                                    
    $name = new lang_string('maximumsubmissionsize', 'assignsubmission_file');                                                      
    $description = new lang_string('configmaxbytes', 'assignsubmission_file');                                                      
                                                                                                                                    
    $element = new admin_setting_configselect('assignsubmission_file/maxbytes',                                                     
                                              $name,                                                                                
                                              $description,                                                                         
                                              1048576,                                                                              
                                              get_max_upload_sizes($CFG->maxbytes));                                                
    $settings->add($element);                                                                                                       
}                                                                          

lang/en/submission_pluginname.php

The language file for this plugin must have the same name as the component name (e.g. "submission_file.php"). It should at least define a string for "pluginname". For example:

$string['pluginname'] = 'Awesome submissions';      

db/access.php

This is where any additional capabilities are defined if required. This file can be omitted if there are no capabilities added by the plugin.

See Activity_modules#access.php for more information.

$capabilities = array(
    'submissionplugin/dungeon:master' => array(
        'riskbitmask' => RISK_XSS,
        'captype' => 'write',
        'contextlevel' => CONTEXT_COURSE,
        'archetypes' => array(
            'editingteacher' => CAP_ALLOW,
            'manager' => CAP_ALLOW
        ),
        'clonepermissionsfrom' => 'moodle/course:manageactivities'
    ),
);

db/upgrade.php

This is where any upgrade code is defined.

See Activity_modules#upgrade.php for more infomation.

function xmldb_submission_file_upgrade($oldversion) {
    global $CFG, $DB, $OUTPUT;

    $dbman = $DB->get_manager();
    if ($oldversion < 2012091800) {
        // Put upgrade code here

        // Savepoint reached.
        upgrade_plugin_savepoint(true, 2012091800, 'assignsubmission', 'file');
    }

    return true;
}

db/install.xml

This is where any database tables required to save this plugins data are defined. File submissions define a table that links to submission and contains a column to record the number of files.

<?xml version="1.0" encoding="UTF-8" ?>                                                                                             
<XMLDB PATH="mod/assign/submission/file/db" VERSION="20120423" COMMENT="XMLDB file for Moodle mod/assign/submission/file"           
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"                                                                           
    xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"                                                              
>                                                                                                                                   
  <TABLES>                                                                                                                          
    <TABLE NAME="assignsubmission_file" COMMENT="Info about file submissions for assignments">                                      
      <FIELDS>                                                                                                                      
        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>                                                    
        <FIELD NAME="assignment" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>                               
        <FIELD NAME="submission" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>                               
        <FIELD NAME="numfiles" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="The number of files the student submitted."/>
      </FIELDS>                                                                                                                     
      <KEYS>                                                                                                                        
        <KEY NAME="primary" TYPE="primary" FIELDS="id" COMMENT="The unique id for this submission info."/>                          
        <KEY NAME="assignment" TYPE="foreign" FIELDS="assignment" REFTABLE="assign" REFFIELDS="id" COMMENT="The assignment instance this submission relates to"/>
        <KEY NAME="submission" TYPE="foreign" FIELDS="submission" REFTABLE="assign_submission" REFFIELDS="id" COMMENT="The submission this file submission relates to."/>
      </KEYS>                                                                                                                       
    </TABLE>                                                                                                                        
  </TABLES>                                                                                                                         
</XMLDB>                           

db/install.php

This example is from the submission_comments plugin. It shows how to run custom code on installation of the plugin. In this case it makes the comments plugin the last of the three submission plugins installed by default.

/**
 * Code run after the module database tables have been created.
 */
function xmldb_submission_comments_install() {
    global $CFG, $DB, $OUTPUT;

    // do the install

    require_once($CFG->dirroot . '/mod/assign/locallib.php');
    // set the correct initial order for the plugins
    $assignment = new assignment();
    $plugin = $assignment->get_submission_plugin_by_type('comments');
    if ($plugin) {
        $plugin->move('down');
        $plugin->move('down');
    }
        
    return true;
}

locallib.php

This is where all the functionality for this plugin is defined. We will step through this file and describe each part as we go.


class assign_submission_file extends assign_submission_plugin {

All submission plugins MUST define a class with the component name of the plugin that extends assign_submission_plugin.


    public function get_name() {
        return get_string('file', 'assignsubmission_file');
    }

Get name is abstract in submission_plugin and must be defined in your new plugin. Use the language strings to make your plugin translatable.

    public function get_settings(MoodleQuickForm $mform) {
       global $CFG, $COURSE;                                                                                                       
                                                                                                                                    
        $defaultmaxfilesubmissions = $this->get_config('maxfilesubmissions');                                                       
        $defaultmaxsubmissionsizebytes = $this->get_config('maxsubmissionsizebytes');                                               
                                                                                                                                    
        $settings = array();                                                                                                        
        $options = array();                                                                                                         
        for ($i = 1; $i <= ASSIGNSUBMISSION_FILE_MAXFILES; $i++) {                                                                  
            $options[$i] = $i;                                                                                                      
        }                                                                                                                           
                                                                                                                                    
        $name = get_string('maxfilessubmission', 'assignsubmission_file');                                                          
        $mform->addElement('select', 'assignsubmission_file_maxfiles', $name, $options);                                            
        $mform->addHelpButton('assignsubmission_file_maxfiles',                                                                     
                              'maxfilessubmission',                                                                                 
                              'assignsubmission_file');                                                                             
        $mform->setDefault('assignsubmission_file_maxfiles', $defaultmaxfilesubmissions);                                           
        $mform->disabledIf('assignsubmission_file_maxfiles', 'assignsubmission_file_enabled', 'notchecked');                        
                                                                                                                                    
        $choices = get_max_upload_sizes($CFG->maxbytes,                                                                             
                                        $COURSE->maxbytes,                                                                          
                                        get_config('assignsubmission_file', 'maxbytes'));                                           
                                                                                                                                    
        $settings[] = array('type' => 'select',                                                                                     
                            'name' => 'maxsubmissionsizebytes',                                                                     
                            'description' => get_string('maximumsubmissionsize', 'assignsubmission_file'),                          
                            'options'=> $choices,                                                                                   
                            'default'=> $defaultmaxsubmissionsizebytes);                                                            
                                                                                                                                    
        $name = get_string('maximumsubmissionsize', 'assignsubmission_file');                                                       
        $mform->addElement('select', 'assignsubmission_file_maxsizebytes', $name, $choices);                                        
        $mform->addHelpButton('assignsubmission_file_maxsizebytes',                                                                 
                              'maximumsubmissionsize',                                                                              
                              'assignsubmission_file');                                                                             
        $mform->setDefault('assignsubmission_file_maxsizebytes', $defaultmaxsubmissionsizebytes);                                   
        $mform->disabledIf('assignsubmission_file_maxsizebytes',                                                                    
                           'assignsubmission_file_enabled',                                                                         
                           'notchecked');                             
    }

The "get_settings" function is called when building the settings page for the assignment. It allows this plugin to add a list of settings to the form. Notice that the settings are prefixed by the plugin name which is good practice to avoid conflicts with other plugins.

    public function save_settings(stdClass $data) {                                                                                 
        $this->set_config('maxfilesubmissions', $data->assignsubmission_file_maxfiles);                                             
        $this->set_config('maxsubmissionsizebytes', $data->assignsubmission_file_maxsizebytes);                                     
        return true;                                                                                                                
    }   

The "save_settings" function is called when the assignment settings page is submitted, either for a new assignment or when editing an existing one. For settings specific to a single instance of the assignment you can use the assign_plugin::set_config function shown here to save key/value pairs against this assignment instance for this plugin.

    public function get_form_elements($submission, MoodleQuickForm $mform, stdClass $data) {                                        
                                                                                                                                    
        if ($this->get_config('maxfilesubmissions') <= 0) {                                                                         
            return false;                                                                                                           
        }                                                                                                                           
                                                                                                                                    
        $fileoptions = $this->get_file_options();                                                                                   
        $submissionid = $submission ? $submission->id : 0;                                                                          
                                                                                                                                    
        $data = file_prepare_standard_filemanager($data,                                                                            
                                                  'files',                                                                          
                                                  $fileoptions,                                                                     
                                                  $this->assignment->get_context(),                                                 
                                                  'assignsubmission_file',                                                          
                                                  ASSIGNSUBMISSION_FILE_FILEAREA,                                                   
                                                  $submissionid);                                                                   
        $mform->addElement('filemanager', 'files_filemanager', html_writer::tag('span', $this->get_name(),                          
            array('class' => 'accesshide')), null, $fileoptions);                                                                   
                                                                                                                                    
        return true;                                                                                                                
    }                                    

The get_form_elements function is called when building the submission form. It functions identically to the get_settings function except that the submission object is available (if there is a submission) to associate the settings with a single submission. This example also shows how to use a filemanager within a submission plugin. The function must return true if it has modified the form otherwise the assignment will not include a header for this plugin.

   public function save(stdClass $submission, stdClass $data) {                                                                    
        global $USER, $DB;                                                                                                          
                                                                                                                                    
        $fileoptions = $this->get_file_options();                                                                                   
                                                                                                                                    
        $data = file_postupdate_standard_filemanager($data,                                                                         
                                                     'files',                                                                       
                                                     $fileoptions,                                                                  
                                                     $this->assignment->get_context(),                                              
                                                     'assignsubmission_file',                                                       
                                                     ASSIGNSUBMISSION_FILE_FILEAREA,                                                
                                                     $submission->id);                                                              
                                                                                                                                    
        $filesubmission = $this->get_file_submission($submission->id);                                                              
                                                                                                                                    
        // Plagiarism code event trigger when files are uploaded.                                                                   
                                                                                                                                    
        $fs = get_file_storage();                                                                                                   
        $files = $fs->get_area_files($this->assignment->get_context()->id,                                                          
                                     'assignsubmission_file',                                                                       
                                     ASSIGNSUBMISSION_FILE_FILEAREA,                                                                
                                     $submission->id,                                                                               
                                     'id',                                                                                          
                                     false);                                                                                        
                                                                                                                                    
        $count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);                                               
                                                                                                                                    
        // Send files to event system.                                                                                              
        // This lets Moodle know that an assessable file was uploaded (eg for plagiarism detection).                                
        $eventdata = new stdClass();                                                                                                
        $eventdata->modulename = 'assign';                                                                                          
        $eventdata->cmid = $this->assignment->get_course_module()->id;                                                              
        $eventdata->itemid = $submission->id;                                                                                       
        $eventdata->courseid = $this->assignment->get_course()->id;                                                                 
        $eventdata->userid = $USER->id;                                                                                             
        if ($count > 1) {                                                                                                           
            $eventdata->files = $files;                                                                                             
        }                                                                                                                           
        $eventdata->file = $files;                                    
        $eventdata->pathnamehashes = array_keys($files);                                                                            
        events_trigger('assessable_file_uploaded', $eventdata);                                                                     
                                                                                                                                    
        if ($filesubmission) {                                                                                                      
            $filesubmission->numfiles = $this->count_files($submission->id,                                                         
                                                           ASSIGNSUBMISSION_FILE_FILEAREA);                                         
            return $DB->update_record('assignsubmission_file', $filesubmission);                                                    
        } else {                                                                                                                    
            $filesubmission = new stdClass();                                                                                       
            $filesubmission->numfiles = $this->count_files($submission->id,                                                         
                                                           ASSIGNSUBMISSION_FILE_FILEAREA);                                         
            $filesubmission->submission = $submission->id;                                                                          
            $filesubmission->assignment = $this->assignment->get_instance()->id;                                                    
            return $DB->insert_record('assignsubmission_file', $filesubmission) > 0;                                                
        }                                

The "save" function is called to save a user submission. The parameters are the submission object and the data from the submission form. This example calls file_postupdate_standard_filemanager to copy the files from the draft file area to the filearea for this submission, it then uses the event api to trigger an assessable_file_uploaded event for the plagiarism api. It then records the number of files in the plugin specific "assignsubmission_file" table.

    public function get_files($submission) {
        $result = array();                                                                                                          
        $fs = get_file_storage();                                                                                                   
                                                                                                                                    
        $files = $fs->get_area_files($this->assignment->get_context()->id,                                                          
                                     'assignsubmission_file',                                                                       
                                     ASSIGNSUBMISSION_FILE_FILEAREA,                                                                
                                     $submission->id,                                                                               
                                     'timemodified',                                                                                
                                     false);                                                                                        
                                                                                                                                    
        foreach ($files as $file) {                                                                                                 
            $result[$file->get_filename()] = $file;                                                                                 
        }                                                                                                                           
        return $result;
    }

If this submission plugin produces one or more files, it should implement "get_files" so that the portfolio API can export a list of all the files from all of the plugins for this assignment submission. This is also used by the offline grading feature in the assignment.

    public function view_summary(stdClass $submission, & $showviewlink) {                                                           
        $count = $this->count_files($submission->id, ASSIGNSUBMISSION_FILE_FILEAREA);                                               
                                                                                                                                    
        // Show we show a link to view all files for this plugin?                                                                   
        $showviewlink = $count > ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES;                                                             
        if ($count <= ASSIGNSUBMISSION_FILE_MAXSUMMARYFILES) {                                                                      
            return $this->assignment->render_area_files('assignsubmission_file',                                                    
                                                        ASSIGNSUBMISSION_FILE_FILEAREA,                                             
                                                        $submission->id);                                                           
        } else {                                                                                                                    
            return get_string('countfiles', 'assignsubmission_file', $count);                                                       
        }                                                                                                                           
    }

The view_summary function is called to display a summary of the submission to both markers and students. It counts the number of files submitted and if it is more that a set number, it only displays a count of how many files are in the submission - otherwise it uses a helper function to write the entire list of files. This is because we want to keep the summaries really short so they can be displayed in a table. There will be a link to view the full submission on the submission status page.

    public function view($submission) {
        return $this->assignment->render_area_files('assignsubmission_file',                                                        
                                                    ASSIGNSUBMISSION_FILE_FILEAREA,                                                 
                                                    $submission->id);
    }

The view function is called to display the entire submission to both markers and students. In this case it uses the helper function in the assignment class to write the list of files.

    public function can_upgrade($type, $version) {

        $uploadsingle_type ='uploadsingle';
        $upload_type ='upload';

        if (($type == $uploadsingle_type || $type == $upload_type) && $version >= 2011112900) {
            return true;
        }
        return false;
    }

The can_upgrade function is used to identify old "Assignment 2.2" subtypes that can be upgraded by this plugin. This plugin supports upgrades from the old "upload" and "uploadsingle" assignment subtypes.

    
    public function upgrade_settings(context $oldcontext, stdClass $oldassignment, & $log) {                                        
        global $DB;                                                                                                                 
                                                                                                                                    
        if ($oldassignment->assignmenttype == 'uploadsingle') {                                                                     
            $this->set_config('maxfilesubmissions', 1);                                                                             
            $this->set_config('maxsubmissionsizebytes', $oldassignment->maxbytes);                                                  
            return true;                                                                                                            
        } else if ($oldassignment->assignmenttype == 'upload') {                                                                    
            $this->set_config('maxfilesubmissions', $oldassignment->var1);                                                          
            $this->set_config('maxsubmissionsizebytes', $oldassignment->maxbytes);                                                  
                                                                                                                                    
            // Advanced file upload uses a different setting to do the same thing.                                                  
            $DB->set_field('assign',                                                                                                
                           'submissiondrafts',                                                                                      
                           $oldassignment->var4,                                                                                    
                           array('id'=>$this->assignment->get_instance()->id));                                                     
                                                                                                                                    
            // Convert advanced file upload "hide description before due date" setting.                                             
            $alwaysshow = 0;                                                                                                        
            if (!$oldassignment->var3) {                                                                                            
                $alwaysshow = 1;                                                                                                    
            }                                                                                                                       
            $DB->set_field('assign',                                                                                                
                           'alwaysshowdescription',                                                                                 
                           $alwaysshow,                                                                                             
                           array('id'=>$this->assignment->get_instance()->id));                                                     
            return true;                                                                                                            
        }                                                                                                                           
    }                     

This function is called once per assignment instance to upgrade the settings from the old assignment to the new mod_assign. In this case it sets the maxbytes, maxfiles and alwaysshowdescription configuration settings.


    public function upgrade($oldcontext,$oldassignment, $oldsubmission, $submission, & $log) {
        global $DB;

        $file_submission = new stdClass();



        $file_submission->numfiles = $oldsubmission->numfiles;
        $file_submission->submission = $submission->id;
        $file_submission->assignment = $this->assignment->get_instance()->id;

        if (!$DB->insert_record('assign_submission_file', $file_submission) > 0) {
            $log .= get_string('couldnotconvertsubmission', 'mod_assign', $submission->userid);
            return false;
        }




        // now copy the area files
        $this->assignment->copy_area_files_for_upgrade($oldcontext->id,
                                                        'mod_assignment',
                                                        'submission',
                                                        $oldsubmission->id,
                                                        // New file area
                                                        $this->assignment->get_context()->id,
                                                        'mod_assign',
                                                        ASSIGN_FILEAREA_SUBMISSION_FILES,
                                                        $submission->id);





        return true;
    }

The "upgrade" function upgrades a single submission from the old assignment type to the new one. In this case it involves copying all the files from the old filearea to the new one. There is a helper function available in the assignment class for this (Note: the copy will be fast as it is just adding rows to the files table). If this function returns false, the upgrade will be aborted and rolled back.

                                                             
    public function upgrade(context $oldcontext,                                                                                    
                            stdClass $oldassignment,                                                                                
                            stdClass $oldsubmission,                                                                                
                            stdClass $submission,                                                                                   
                            & $log) {                                                                                               
        global $DB;                                                                                                                 
                                                                                                                                    
        $filesubmission = new stdClass();                                                                                           
                                                                                                                                    
        $filesubmission->numfiles = $oldsubmission->numfiles;                                                                       
        $filesubmission->submission = $submission->id;                                                                              
        $filesubmission->assignment = $this->assignment->get_instance()->id;                                                        
                                                                                                                                    
        if (!$DB->insert_record('assignsubmission_file', $filesubmission) > 0) {                                                    
            $log .= get_string('couldnotconvertsubmission', 'mod_assign', $submission->userid);                                     
            return false;                                                                                                           
        }                                                                                                                           
                                                                                                                                    
        // Now copy the area files.                                                                                                 
        $this->assignment->copy_area_files_for_upgrade($oldcontext->id,                                                             
                                                        'mod_assignment',                                                           
                                                        'submission',                                                               
                                                        $oldsubmission->id,                                                         
                                                        $this->assignment->get_context()->id,                                       
                                                        'assignsubmission_file',                                                    
                                                        ASSIGNSUBMISSION_FILE_FILEAREA,                                             
                                                        $submission->id);                                                           
                                                                                                                                    
        return true;                                                                                                                
    }                              

This example is from assignsubmission_onlinetext. If the plugin uses a text-editor it is ideal if the plugin implements "get_editor_fields". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. If a plugin supports multiple text areas it can return the name of each of them here.

    public function get_editor_fields() {                                                                                           
        return array('onlinetext' => get_string('pluginname', 'assignsubmission_comments'));                                        
    }       

This example is from assignsubmission_onlinetext. If the plugin uses a text-editor it is ideal if the plugin implements "get_editor_text". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. The name is used to distinguish between multiple text areas in the one plugin.

    public function get_editor_text($name, $submissionid) {                                                                         
        if ($name == 'onlinetext') {                                                                                                
            $onlinetextsubmission = $this->get_onlinetext_submission($submissionid);                                                
            if ($onlinetextsubmission) {                                                                                            
                return $onlinetextsubmission->onlinetext;                                                                           
            }                                                                                                                       
        }                                                                                                                           
                                                                                                                                    
        return '';                                                                                                                  
    }             

This example is from assignsubmission_onlinetext. For the same reason as the previous function, if the plugin uses a text editor, it is ideal if the plugin implements "get_editor_format". This allows the portfolio to retrieve the text from the plugin when exporting the list of files for a submission. This is required because the text is stored in the plugin specific table that is only known to the plugin itself. The name is used to distinguish between multiple text areas in the one plugin.

    public function get_editor_format($name, $submissionid) {
        if ($name == 'onlinetext') {
            $onlinetext_submission = $this->get_onlinetext_submission($submissionid);
            if ($onlinetext_submission) {
                return $onlinetext_submission->onlineformat;
            }
        }

        return 0;
    }