Пример #1
0
 /**
  * Checks the decsep and thousandssep config strings are set correctly
  *
  * @link http://tracker.moodle.org/browse/MDL-31332
  * @return bool check success flag
  */
 protected function check_decsep_thousandssep()
 {
     $details = array();
     $langnames = array();
     $tree = mlang_tools::components_tree(array('component' => 'langconfig'));
     foreach ($tree as $branch => $languages) {
         $version = mlang_version::by_code($branch);
         foreach (array_keys($languages) as $language) {
             $langconfig = mlang_component::from_snapshot('langconfig', $language, $version);
             if ($langname = $langconfig->get_string('thislanguageint')) {
                 $langnames[$language] = $langname->text;
             } else {
                 $langnames[$language] = $language;
             }
             if ($decsep = $langconfig->get_string('decsep')) {
                 $decsep = $decsep->text;
             }
             if ($thousandssep = $langconfig->get_string('thousandssep')) {
                 $thousandssep = $thousandssep->text;
             }
             if (empty($decsep) and empty($thousandssep)) {
                 $details[$language][$version->label] = 1;
             } else {
                 if (empty($decsep) or empty($thousandssep)) {
                     $details[$language][$version->label] = 2;
                 } else {
                     if ($decsep === $thousandssep) {
                         $details[$language][$version->label] = 3;
                     }
                 }
             }
         }
     }
     ksort($details);
     foreach ($details as $language => $branches) {
         $msg = sprintf('Invalid decsep and/or thousandssep in %s {%s} at', $langnames[$language], $language);
         foreach ($branches as $branch => $severity) {
             $msg .= ' ' . $branch . str_repeat('!', $severity);
         }
         $this->output($msg, true);
     }
     if (empty($details)) {
         return 0;
     }
     return 1;
 }
Пример #2
0
 function definition()
 {
     global $USER;
     $mform = $this->_form;
     // Commits filter
     $mform->addElement('header', 'logfiltercommits', get_string('logfiltercommits', 'local_amos'));
     // Committed after
     $mform->addElement('date_time_selector', 'committedafter', get_string('logfiltercommittedafter', 'local_amos'), array('optional' => true, 'timezone' => 'UTC', 'applydst' => false));
     $mform->setDefault('committedafter', $USER->lastlogin);
     // Committed before
     $mform->addElement('date_time_selector', 'committedbefore', get_string('logfiltercommittedbefore', 'local_amos'), array('optional' => true, 'timezone' => 'UTC', 'applydst' => false));
     $mform->setAdvanced('committedbefore');
     // Committer
     $committers = get_users_by_capability(get_system_context(), 'local/amos:commit', user_picture::fields('u'), 'lastname, firstname');
     $users = array('' => '');
     foreach ($committers as $committer) {
         $users[$committer->id] = s(fullname($committer) . ' <' . $committer->email . '>');
     }
     $usergrp[] = $mform->createElement('select', 'userid', '', $users);
     $usergrp[] = $mform->createElement('text', 'userinfo', '');
     $mform->setType('userinfo', PARAM_NOTAGS);
     $mform->addGroup($usergrp, 'usergrp', get_string('logfilterusergrp', 'local_amos'), get_string('logfilterusergrpor', 'local_amos'), false);
     $mform->setAdvanced('usergrp');
     // Source
     $sources = array('' => '', 'git' => get_string('logfiltersourcegit', 'local_amos'), 'amos' => get_string('logfiltersourceamos', 'local_amos'), 'revclean' => get_string('logfiltersourcerevclean', 'local_amos'), 'commitscript' => get_string('logfiltersourcecommitscript', 'local_amos'), 'fixdrift' => get_string('logfiltersourcefixdrift', 'local_amos'), 'bot' => get_string('logfiltersourcebot', 'local_amos'));
     $mform->addElement('select', 'source', get_string('logfiltersource', 'local_amos'), $sources);
     $mform->setAdvanced('source');
     // Commit message
     $mform->addElement('text', 'commitmsg', get_string('logfiltercommitmsg', 'local_amos'));
     $mform->setAdvanced('commitmsg');
     // Commit hash
     $mform->addElement('text', 'commithash', get_string('logfiltercommithash', 'local_amos'));
     $mform->setAdvanced('commithash');
     // Strings filter
     $mform->addElement('header', 'logfilterstrings', get_string('logfilterstrings', 'local_amos'));
     // Branch
     $branchgrp = array();
     foreach (mlang_version::list_all() as $version) {
         $branchgrp[] = $mform->createElement('checkbox', $version->code, '', $version->label);
     }
     $mform->addGroup($branchgrp, 'branch', get_string('logfilterbranch', 'local_amos'), ' ');
     foreach (mlang_version::list_all() as $version) {
         if ($version->current) {
             $mform->setDefault('branch[' . $version->code . ']', 1);
         }
     }
     // Lang
     $langgrp = array();
     $langgrp[] = $mform->createElement('checkbox', 'langenabled', '', get_string('enable'));
     $langgrp[] = $mform->createElement('select', 'lang', '', mlang_tools::list_languages(), array('multiple' => 'multiple', 'size' => 5));
     $mform->addGroup($langgrp, 'langgrp', get_string('logfilterlang', 'local_amos'), '<br />', false);
     $mform->setDefault('lang', array('en', current_language()));
     $mform->disabledIf('lang', 'langenabled');
     // does not seem to work for multiple selects :-/
     $mform->setAdvanced('langgrp');
     // Components
     $optionscore = array();
     $optionsstandard = array();
     $optionscontrib = array();
     $standard = array();
     foreach (local_amos_standard_plugins() as $plugins) {
         $standard = array_merge($standard, $plugins);
     }
     foreach (mlang_tools::list_components() as $componentname => $undefined) {
         if (isset($standard[$componentname])) {
             if ($standard[$componentname] === 'core' or substr($standard[$componentname], 0, 5) === 'core_') {
                 $optionscore[$componentname] = $standard[$componentname];
             } else {
                 $optionsstandard[$componentname] = $standard[$componentname];
             }
         } else {
             $optionscontrib[$componentname] = $componentname;
         }
     }
     asort($optionscore);
     asort($optionsstandard);
     asort($optionscontrib);
     $options = array(get_string('pluginclasscore', 'local_amos') => $optionscore, get_string('pluginclassstandard', 'local_amos') => $optionsstandard, get_string('pluginclassnonstandard', 'local_amos') => $optionscontrib);
     $componentgrp = array();
     $componentgrp[] = $mform->createElement('checkbox', 'componentenabled', '', get_string('enable'));
     $componentgrp[] = $mform->createElement('selectgroups', 'component', '', $options, array('multiple' => 'multiple', 'size' => 5));
     $mform->addGroup($componentgrp, 'componentgrp', get_string('logfiltercomponent', 'local_amos'), '<br />', false);
     $mform->disabledIf('component', 'componentenabled');
     // does not seem to work for multiple selects :-/
     $mform->setAdvanced('componentgrp');
     // Stringid
     $mform->addElement('text', 'stringid', get_string('logfilterstringid', 'local_amos'));
     $mform->setType('stringid', PARAM_STRINGID);
     $mform->setAdvanced('stringid');
     // Submit
     $mform->addElement('submit', 'submit', get_string('logfiltershow', 'local_amos'));
     $mform->closeHeaderBefore('submit');
 }
Пример #3
0
 */
require_once dirname(dirname(dirname(__FILE__))) . '/config.php';
require_once dirname(__FILE__) . '/locallib.php';
require_once dirname(__FILE__) . '/mlanglib.php';
require_once dirname(__FILE__) . '/execute_form.php';
require_login(SITEID, false);
require_capability('local/amos:execute', get_system_context());
require_capability('local/amos:stage', get_system_context());
$PAGE->set_pagelayout('standard');
$PAGE->set_url('/local/amos/execute.php');
navigation_node::override_active_url(new moodle_url('/local/amos/stage.php'));
$PAGE->set_title('AMOS ' . get_string('scriptexecute', 'local_amos'));
$PAGE->set_heading('AMOS ' . get_string('scriptexecute', 'local_amos'));
$executeform = new local_amos_execute_form(null, local_amos_execute_options());
if ($data = $executeform->get_data()) {
    $version = mlang_version::by_code($data->version);
    $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
    $instructions = mlang_tools::extract_script_from_text($data->script);
    if (!empty($instructions)) {
        foreach ($instructions as $instruction) {
            $changes = mlang_tools::execute($instruction, $version);
            if ($changes instanceof mlang_stage) {
                foreach ($changes->get_iterator() as $component) {
                    $stage->add($component, true);
                }
                $changes->clear();
            } elseif ($changes < 0) {
                throw new moodle_exception('error_during_amoscript_execution', 'local_amos', '', null, $changes);
            }
            unset($changes);
        }
Пример #4
0
require_once dirname(__FILE__) . '/locallib.php';
require_once dirname(__FILE__) . '/mlanglib.php';
require_once dirname(__FILE__) . '/merge_form.php';
require_login(SITEID, false);
require_capability('local/amos:commit', get_system_context());
// for langpack maintainers only
$PAGE->set_pagelayout('standard');
$PAGE->set_url('/local/amos/merge.php');
navigation_node::override_active_url(new moodle_url('/local/amos/stage.php'));
$PAGE->set_title('AMOS ' . get_string('merge', 'local_amos'));
$PAGE->set_heading('AMOS ' . get_string('merge', 'local_amos'));
$mergeform = new local_amos_merge_form(null, local_amos_merge_options());
if ($data = $mergeform->get_data()) {
    $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
    $sourceversion = mlang_version::by_code($data->sourceversion);
    $targetversion = mlang_version::by_code($data->targetversion);
    if (is_null($sourceversion) or is_null($targetversion)) {
        notice('Invalid version selected', new moodle_url('/local/amos/stage.php'));
    }
    $tree = mlang_tools::components_tree(array('branch' => $sourceversion->code, 'lang' => $data->language));
    $sourcecomponentnames = array_keys(reset(reset($tree)));
    unset($tree);
    foreach ($sourcecomponentnames as $sourcecomponentname) {
        // get a snapshot of both components and merge source into target
        $sourcecomponent = mlang_component::from_snapshot($sourcecomponentname, $data->language, $sourceversion);
        $targetcomponent = mlang_component::from_snapshot($sourcecomponent->name, $sourcecomponent->lang, $targetversion);
        mlang_tools::merge($sourcecomponent, $targetcomponent);
        $sourcecomponent->clear();
        // keep just strings that are defined in english
        $englishcomponent = mlang_component::from_snapshot($sourcecomponent->name, 'en', $targetversion);
        $targetcomponent->intersect($englishcomponent);
Пример #5
0
 public function test_php_parser_security_variable_expansion()
 {
     // security issues
     $parser = mlang_parser_factory::get_parser('php');
     $data = '<?php $string[\'dbpass\'] = $CFG->dbpass;';
     // this would give the user sensitive data about AMOS portal
     $component = new mlang_component('test', 'xx', mlang_version::by_branch('MOODLE_20_STABLE'));
     $this->expectException('mlang_parser_exception');
     $parser->parse($data, $component);
 }
Пример #6
0
require_once dirname(__FILE__) . '/diff_form.php';
require_login(SITEID, false);
require_capability('local/amos:stage', get_system_context());
$PAGE->set_pagelayout('standard');
$PAGE->set_url('/local/amos/diff.php');
navigation_node::override_active_url(new moodle_url('/local/amos/stage.php'));
$PAGE->set_title('AMOS ' . get_string('diff', 'local_amos'));
$PAGE->set_heading('AMOS ' . get_string('diff', 'local_amos'));
$diffform = new local_amos_diff_form(null, local_amos_diff_options());
if ($data = $diffform->get_data()) {
    if ($data->versiona == $data->versionb) {
        notice(get_string('nodiffs', 'local_amos'), new moodle_url('/local/amos/stage.php'));
    }
    $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
    $versiona = mlang_version::by_code($data->versiona);
    $versionb = mlang_version::by_code($data->versionb);
    $tree = mlang_tools::components_tree(array('branch' => $versiona->code, 'lang' => 'en'));
    $componentnames = array_keys($tree[$versiona->code]['en']);
    $total = count($componentnames);
    unset($tree);
    echo $OUTPUT->header();
    $progressbar = new progress_bar();
    $progressbar->create();
    // prints the HTML code of the progress bar
    // we may need a bit of extra execution time and memory here
    @set_time_limit(HOURSECS);
    raise_memory_limit(MEMORY_EXTRA);
    // number of differences
    $num = 0;
    foreach ($componentnames as $i => $componentname) {
        $progressbar->update($i, $total, get_string('diffprogress', 'local_amos'));
Пример #7
0
 * @subpackage amos
 * @copyright  2011 David Mudrak <*****@*****.**>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
define('CLI_SCRIPT', true);
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php';
require_once $CFG->dirroot . '/local/amos/cli/config.php';
require_once $CFG->dirroot . '/local/amos/mlanglib.php';
require_once $CFG->libdir . '/clilib.php';
list($options, $unrecognized) = cli_get_params(array('from' => false, 'to' => false, 'help' => false), array('h' => 'help'));
if ($options['help'] or !$options['from'] or !$options['to']) {
    echo 'Usage: ' . basename(__FILE__) . ' --from=MOODLE_XX_STABLE --to=MOODLE_YY_STABLE' . PHP_EOL;
    exit(1);
}
$fromversion = mlang_version::by_branch($options['from']);
$toversion = mlang_version::by_branch($options['to']);
if (is_null($fromversion) or is_null($toversion)) {
    echo 'Unknown branch' . PHP_EOL;
    exit(2);
}
// Make sure that this is not executed by mistake
if ($DB->record_exists('amos_repository', array('branch' => $toversion->code))) {
    echo 'The target branch already exists' . PHP_EOL;
    exit(3);
}
// Let us get the list of commits that change strings on the given branch
$sql = "SELECT DISTINCT commitid\n          FROM {amos_repository}\n         WHERE branch = ?\n      ORDER BY commitid";
$rs = $DB->get_recordset_sql($sql, array($fromversion->code));
foreach ($rs as $record) {
    $commitid = $record->commitid;
    echo 'cloning changes introduced by commit ' . $commitid . ' ... ';
Пример #8
0
    header('HTTP/1.1 403 Forbidden');
    die;
}
if (!confirm_sesskey()) {
    header('HTTP/1.1 403 Forbidden');
    die;
}
$stringid = optional_param('stringid', null, PARAM_ALPHANUMEXT);
$text = optional_param('text', null, PARAM_RAW);
if (is_null($stringid) or is_null($text)) {
    header('HTTP/1.1 400 Bad Request');
    die;
}
list($lang, $originalid, $translationid) = local_amos_translator::decode_identifier($stringid);
$record = $DB->get_record('amos_repository', array('id' => $originalid), 'id,stringid,component,branch', MUST_EXIST);
$version = mlang_version::by_code($record->branch);
$component = new mlang_component($record->component, $lang, $version);
if ($version->code < mlang_version::MOODLE_20) {
    header('HTTP/1.1 400 Bad Request');
    die;
}
$string = new mlang_string($record->stringid, $text);
$string->clean_text();
$component->add_string($string);
$stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
$stage->add($component, true);
$stage->store();
mlang_stash::autosave($stage);
header('Content-Type: application/json; charset: utf-8');
$response = new stdclass();
$response->text = local_amos_renderer::add_breaks(s($string->text));
Пример #9
0
The file is directly included into the PHP processor. Make sure to review the file
for a malicious contents before you import it via this script.

EOF;
if ($options['help'] or empty($options['message']) or empty($unrecognized)) {
    echo $usage . PHP_EOL;
    exit(1);
}
$filepath = $unrecognized[0];
if (!is_readable($filepath)) {
    echo 'File not readable' . PHP_EOL;
    echo $usage . PHP_EOL;
    exit(2);
}
$version = mlang_version::by_branch($options['version']);
if (is_null($version)) {
    echo 'Invalid version' . PHP_EOL;
    exit(3);
}
$component = mlang_component::from_phpfile($filepath, $options['lang'], $version, $options['timemodified'], $options['name'], (int) $options['format']);
fputs(STDOUT, "{$component->name} {$component->version->label} {$component->lang}" . PHP_EOL);
$stage = new mlang_stage();
$stage->add($component);
$stage->rebase(null, true, $options['timemodified']);
if (!$stage->has_component()) {
    echo 'No strings found (after rebase)' . PHP_EOL;
    exit(4);
}
foreach ($stage->get_iterator() as $component) {
    foreach ($component->get_iterator() as $string) {
Пример #10
0
 /**
  * Fetches the required commits from the repository
  *
  * @param array $filter allows to filter commits
  */
 public function __construct(array $filter = array())
 {
     global $DB;
     // we can not use limits inside subquery so firstly let us get commits we are interested in
     $params = array();
     $where = array();
     $getsql = "SELECT id";
     $countsql = "SELECT COUNT(*)";
     $sql = "  FROM {amos_commits}";
     if (!empty($filter['userid'])) {
         $where['userid'] = "userid = ?";
         $params[] = $filter['userid'];
     }
     if (!empty($filter['userinfo'])) {
         $where['userinfo'] = $DB->sql_like('userinfo', '?', false, false);
         $params[] = '%' . $filter['userinfo'] . '%';
     }
     if (!empty($where['userinfo']) and !empty($where['userid'])) {
         $where['user'] = '******' . $where['userid'] . ') OR (' . $where['userinfo'] . ')';
         unset($where['userinfo']);
         unset($where['userid']);
     }
     if (!empty($filter['committedafter'])) {
         $where['committedafter'] = 'timecommitted >= ?';
         $params[] = $filter['committedafter'];
     }
     if (!empty($filter['committedbefore'])) {
         $where['committedbefore'] = 'timecommitted < ?';
         $params[] = $filter['committedbefore'];
     }
     if (!empty($filter['source'])) {
         $where['source'] = 'source = ?';
         $params[] = $filter['source'];
     }
     if (!empty($filter['commitmsg'])) {
         $where['commitmsg'] = $DB->sql_like('commitmsg', '?', false, false);
         $params[] = '%' . $filter['commitmsg'] . '%';
     }
     if (!empty($filter['commithash'])) {
         $where['commithash'] = $DB->sql_like('commithash', '?', false, false);
         $params[] = $filter['commithash'] . '%';
     }
     if ($where) {
         $where = '(' . implode(') AND (', $where) . ')';
         $sql .= " WHERE {$where}";
     }
     $ordersql = " ORDER BY timecommitted DESC, id DESC";
     $this->numofcommits = $DB->count_records_sql($countsql . $sql, $params);
     $commitids = $DB->get_records_sql($getsql . $sql . $ordersql, $params, 0, self::LIMITCOMMITS);
     if (empty($commitids)) {
         // nothing to load
         return;
     }
     // now get all repository records modified by these commits
     // and optionally filter them if requested
     $params = array();
     list($csql, $params) = $DB->get_in_or_equal(array_keys($commitids));
     if (!empty($filter['branch'])) {
         list($branchsql, $branchparams) = $DB->get_in_or_equal(array_keys($filter['branch']));
     } else {
         $branchsql = '';
     }
     if (!empty($filter['lang'])) {
         list($langsql, $langparams) = $DB->get_in_or_equal($filter['lang']);
     } else {
         $langsql = '';
     }
     if (!empty($filter['component'])) {
         list($componentsql, $componentparams) = $DB->get_in_or_equal($filter['component']);
     } else {
         $componentsql = '';
     }
     $countsql = "SELECT COUNT(r.id)";
     $getsql = "SELECT r.id, c.source, c.timecommitted, c.commitmsg, c.commithash, c.userid, c.userinfo,\n                              r.commitid, r.branch, r.lang, r.component, r.stringid, r.text, r.timemodified, r.deleted";
     $sql = "  FROM {amos_commits} c\n                         JOIN {amos_repository} r ON (c.id = r.commitid)\n                        WHERE c.id {$csql}";
     if ($branchsql) {
         $sql .= " AND r.branch {$branchsql}";
         $params = array_merge($params, $branchparams);
     }
     if ($langsql) {
         $sql .= " AND r.lang {$langsql}";
         $params = array_merge($params, $langparams);
     }
     if ($componentsql) {
         $sql .= " AND r.component {$componentsql}";
         $params = array_merge($params, $componentparams);
     }
     if (!empty($filter['stringid'])) {
         $sql .= " AND r.stringid = ?";
         $params[] = $filter['stringid'];
     }
     $ordersql = " ORDER BY c.timecommitted DESC, c.id DESC, r.branch DESC, r.lang, r.component, r.stringid";
     $this->numofstrings = $DB->count_records_sql($countsql . $sql, $params);
     $rs = $DB->get_recordset_sql($getsql . $sql . $ordersql, $params);
     $numofcommits = 0;
     foreach ($rs as $r) {
         if (!isset($this->commits[$r->commitid])) {
             if ($numofcommits == self::LIMITCOMMITS) {
                 // we already have enough
                 break;
             }
             $commit = new stdclass();
             $commit->id = $r->commitid;
             $commit->source = $r->source;
             $commit->timecommitted = $r->timecommitted;
             $commit->commitmsg = $r->commitmsg;
             $commit->commithash = $r->commithash;
             $commit->userid = $r->userid;
             $commit->userinfo = $r->userinfo;
             $commit->strings = array();
             $this->commits[$r->commitid] = $commit;
             $numofcommits++;
         }
         $string = new stdclass();
         $string->branch = mlang_version::by_code($r->branch)->label;
         $string->component = $r->component;
         $string->lang = $r->lang;
         $string->stringid = $r->stringid;
         $string->deleted = $r->deleted;
         $this->commits[$r->commitid]->strings[] = $string;
     }
     $rs->close();
 }
Пример #11
0
define('AJAX_SCRIPT', true);
require_once dirname(dirname(dirname(__FILE__))) . '/config.php';
//require_once(dirname(__FILE__).'/locallib.php');
require_once dirname(__FILE__) . '/mlanglib.php';
require_login(SITEID, false);
if (!has_capability('local/amos:stage', get_system_context())) {
    header('HTTP/1.1 403 Forbidden');
    die;
}
if (!confirm_sesskey()) {
    header('HTTP/1.1 403 Forbidden');
    die;
}
$name = required_param('component', PARAM_ALPHANUMEXT);
$branch = required_param('branch', PARAM_INT);
$lang = required_param('lang', PARAM_ALPHANUMEXT);
$unstage = required_param('unstage', PARAM_STRINGID);
$response = new stdClass();
$response->success = true;
$response->error = '';
$stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
$component = $stage->get_component($name, $lang, mlang_version::by_code($branch));
if ($component) {
    $component->unlink_string($unstage);
    $stage->store();
} else {
    $response->success = false;
    $response->error = 'Unable to get load component';
}
header('Content-Type: application/json; charset: utf-8');
echo json_encode($response);
Пример #12
0
 public function test_stage_propagate()
 {
     $version20 = mlang_version::by_branch('MOODLE_20_STABLE');
     $version21 = mlang_version::by_branch('MOODLE_21_STABLE');
     $version22 = mlang_version::by_branch('MOODLE_22_STABLE');
     $component20en = new mlang_component('admin', 'en', $version20);
     $component20en->add_string(new mlang_string('foo1', 'Bar1'));
     $component20en->add_string(new mlang_string('foo2', 'Bar2'));
     $component20cs = new mlang_component('admin', 'cs', $version20);
     $component20cs->add_string(new mlang_string('foo1', 'TranslatedOldBar1'));
     $component21en = new mlang_component('admin', 'en', $version21);
     $component21en->add_string(new mlang_string('foo1', 'Bar1'));
     $component21en->add_string(new mlang_string('foo2', 'Bar2'));
     $component21en->add_string(new mlang_string('foo3', 'Bar3'));
     $component22en = new mlang_component('admin', 'en', $version22);
     $component22en->add_string(new mlang_string('foo1', 'Bar1'));
     $component22en->add_string(new mlang_string('foo2', 'Bar2'));
     $component22en->add_string(new mlang_string('foo3', 'NewBar3'));
     $stage = new mlang_stage();
     $stage->add($component20en);
     $stage->add($component20cs);
     $stage->add($component21en);
     $stage->add($component22en);
     $stage->commit('Initial strings', array('source' => 'unittest'), true);
     $component20en->clear();
     $component21en->clear();
     $component22en->clear();
     unset($stage);
     // simple usage - the user translated a string on 2.1 and want it being applied to 2.2, too
     $stage = new mlang_stage();
     $component21cs = new mlang_component('admin', 'cs', $version21);
     $component21cs->add_string(new mlang_string('foo1', 'TranslatedBar1'));
     $stage->add($component21cs);
     $component21cs->clear();
     $this->assertEqual($stage->propagate(array($version22)), 1);
     $propagatedcomponent = $stage->get_component('admin', 'cs', $version22);
     $this->assertNotNull($propagatedcomponent, 'The component "admin" must exist on ' . $version22->label);
     $this->assertTrue($propagatedcomponent->has_string('foo1'));
     $propagatedstring = $propagatedcomponent->get_string('foo1');
     $this->assertTrue($propagatedstring->text, 'TranslatedBar1');
     $stage->clear();
     // the change is not propagated if the changed string is staged several times and the values
     // are different
     $stage = new mlang_stage();
     $component20cs = new mlang_component('admin', 'cs', $version20);
     $component20cs->add_string(new mlang_string('foo2', 'TranslatedOldBar2'));
     $component21cs = new mlang_component('admin', 'cs', $version21);
     $component21cs->add_string(new mlang_string('foo2', 'TranslatedBar2'));
     $stage->add($component20cs);
     $stage->add($component21cs);
     $component20cs->clear();
     $component21cs->clear();
     $this->assertEqual($stage->propagate(array($version20, $version21, $version22)), 0);
     $this->assertEqual($stage->get_component('admin', 'cs', $version20)->get_string('foo2')->text, 'TranslatedOldBar2');
     $this->assertEqual($stage->get_component('admin', 'cs', $version21)->get_string('foo2')->text, 'TranslatedBar2');
     $this->assertNull($stage->get_component('admin', 'cs', $version22));
     $stage->clear();
     // but the change is propagated if the changed string is staged several times and the values
     // are the same
     $stage = new mlang_stage();
     $component20cs = new mlang_component('admin', 'cs', $version20);
     $component20cs->add_string(new mlang_string('foo2', 'TranslatedBar2'));
     $component21cs = new mlang_component('admin', 'cs', $version21);
     $component21cs->add_string(new mlang_string('foo2', 'TranslatedBar2'));
     $stage->add($component20cs);
     $stage->add($component21cs);
     $component20cs->clear();
     $component21cs->clear();
     $this->assertEqual($stage->propagate(array($version20, $version21, $version22)), 1);
     $this->assertEqual($stage->get_component('admin', 'cs', $version22)->get_string('foo2')->text, 'TranslatedBar2');
     $this->assertEqual($stage->get_component('admin', 'cs', $version21)->get_string('foo2')->text, 'TranslatedBar2');
     $this->assertEqual($stage->get_component('admin', 'cs', $version20)->get_string('foo2')->text, 'TranslatedBar2');
     $stage->clear();
     // the staged string is propagated to another branch only if the English originals match
     // in the following test, the 2.1 translation of foo3 should not propagate to neither 2.0
     // (because the string does not exist there) not 2.2 (because the English originals differ)
     $stage = new mlang_stage();
     $component21cs = new mlang_component('admin', 'cs', $version21);
     $component21cs->add_string(new mlang_string('foo3', 'TranslatedBar3'));
     $stage->add($component21cs);
     $component21cs->clear();
     $this->assertEqual($stage->propagate(array($version20, $version21, $version22)), 0);
     $this->assertNull($stage->get_component('admin', 'cs', $version20));
     $this->assertNull($stage->get_component('admin', 'cs', $version22));
     $stage->clear();
 }
Пример #13
0
 * Register new language
 *
 * @package   local-amos
 * @copyright 2010 David Mudrak <*****@*****.**>
 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php';
require_once dirname(dirname(__FILE__)) . '/mlanglib.php';
require_once dirname(__FILE__) . '/newlanguage_form.php';
require_login(SITEID, false);
require_capability('local/amos:manage', get_system_context());
$PAGE->set_pagelayout('standard');
$PAGE->set_url('/local/amos/admin/newlanguage.php');
$PAGE->set_title('AMOS ' . get_string('newlanguage', 'local_amos'));
$PAGE->set_heading('AMOS ' . get_string('newlanguage', 'local_amos'));
$form = new local_amos_newlanguage_form();
if ($data = $form->get_data()) {
    $component = new mlang_component('langconfig', $data->langcode, mlang_version::by_code(mlang_version::MOODLE_22));
    $data->langname = mlang_string::fix_syntax($data->langname);
    $data->langnameint = mlang_string::fix_syntax($data->langnameint);
    $component->add_string(new mlang_string('thislanguage', $data->langname));
    $component->add_string(new mlang_string('thislanguageint', $data->langnameint));
    $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
    $stage->add($component);
    $stage->store();
    redirect(new moodle_url('/local/amos/stage.php'));
}
/// Output starts here
echo $OUTPUT->header();
$form->display();
echo $OUTPUT->footer();
Пример #14
0
if (!empty($submit)) {
    require_sesskey();
    $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
    $stash = mlang_stash::instance_from_stage($stage, $stage->userid);
    $stash->push();
    redirect(new moodle_url('/local/amos/stash.php', array('sesskey' => sesskey(), 'submit' => $stash->id)));
}
if (!empty($propagate)) {
    require_sesskey();
    $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey());
    $ver = optional_param('ver', array(), PARAM_INT);
    $num = null;
    if (!empty($ver) and is_array($ver)) {
        $versions = array();
        foreach ($ver as $versioncode) {
            $versions[] = mlang_version::by_code($versioncode);
        }
        if ($versions) {
            $num = $stage->propagate($versions);
            $stage->store();
        }
    }
    if (is_null($num)) {
        redirect($PAGE->url);
    } else {
        redirect(new moodle_url($PAGE->url, array('justpropagated' => $num)));
    }
}
$output = $PAGE->get_renderer('local_amos');
// make sure that USER contains sesskey property
$sesskey = sesskey();
Пример #15
0
define('CLI_SCRIPT', true);
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php';
require_once $CFG->dirroot . '/local/amos/cli/config.php';
require_once $CFG->dirroot . '/local/amos/mlanglib.php';
// Let us get an information about existing components
$sql = "SELECT branch,lang,component,COUNT(stringid) AS numofstrings\n          FROM {amos_repository}\n         WHERE deleted=0\n      GROUP BY branch,lang,component\n      ORDER BY branch,lang,component";
$rs = $DB->get_recordset_sql($sql);
$tree = array();
// [branch][language][component] => numofstrings
foreach ($rs as $record) {
    $tree[$record->branch][$record->lang][$record->component] = $record->numofstrings;
}
$rs->close();
remove_dir(AMOS_EXPORT_DIR, true);
foreach ($tree as $vercode => $languages) {
    $version = mlang_version::by_code($vercode);
    foreach ($languages as $langcode => $components) {
        if ($langcode == 'en') {
            continue;
        }
        foreach ($components as $componentname => $unused) {
            $component = mlang_component::from_snapshot($componentname, $langcode, $version);
            if ($component->has_string()) {
                $file = AMOS_EXPORT_DIR . '/' . $version->dir . '/' . $langcode . '/' . $component->get_phpfile_location(false);
                if (!file_exists(dirname($file))) {
                    mkdir(dirname($file), 0755, true);
                }
                echo "{$file}\n";
                $component->export_phpfile($file);
            }
            $component->clear();
Пример #16
0
 * @package    local
 * @subpackage amos
 * @copyright  2011 David Mudrak <*****@*****.**>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
define('CLI_SCRIPT', true);
require_once dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php';
require_once $CFG->dirroot . '/local/amos/cli/config.php';
require_once $CFG->dirroot . '/local/amos/mlanglib.php';
require_once $CFG->dirroot . '/local/amos/locallib.php';
require_once $CFG->libdir . '/clilib.php';
list($options, $unrecognized) = cli_get_params(array('execute' => false));
$plugins = local_amos_standard_plugins();
$stage = new mlang_stage();
foreach ($plugins as $versionnumber => $plugintypes) {
    $version = mlang_version::by_dir($versionnumber);
    if ($version->branch == 'MOODLE_23_STABLE') {
        $gitbranch = 'origin/master';
    } else {
        $gitbranch = 'origin/' . $version->branch;
    }
    foreach ($plugintypes as $legacyname => $frankenstylename) {
        // moodle.org was replaced with a local plugin and strings were dropped from 2.0 and 2.1
        if ($legacyname == 'moodle.org') {
            continue;
        }
        // prepare an empty component containing the fixes
        $fixcomponent = new mlang_component($legacyname, 'en', $version);
        // get the most recent snapshot from the AMOS repository
        $amoscomponent = mlang_component::from_snapshot($legacyname, 'en', $version);
        // get the location of the plugin
Пример #17
0
     // whole file removal - xxx what can be done? :-/
     continue;
 }
 // dump the given revision of the file to a temporary area
 $checkout = $commithash . '_' . str_replace('/', '_', $file);
 if (in_array($checkout, $MLANG_BROKEN_CHECKOUTS)) {
     fputs(STDERR, "BROKEN {$checkout}\n");
     continue;
 }
 $checkout = $tmp . '/' . $checkout;
 exec(AMOS_PATH_GIT . " show {$commithash}:{$file} > {$checkout}");
 // push the string on all branches where the English original is currently (or has ever been) defined
 // note that all English strings history must already be registered in AMOS repository
 // pushing into Moodle 1.x branches only to prevent conflicts with translations done via web
 foreach (array('MOODLE_19_STABLE') as $branch) {
     $version = mlang_version::by_branch($branch);
     // get the translated strings from PHP file - the lang repository in in 1.x format
     $component = mlang_component::from_phpfile($checkout, $langcode, $version, $timemodified, mlang_component::name_from_filename($file), 1);
     $encomponent = mlang_component::from_snapshot($component->name, 'en', $version, $timemodified);
     // keep just those defined in English on that branch - this is where we are reconstruct branching of lang packs.
     // langconfig.php is not compared with English because it may contain extra string like parentlanguage.
     if ($component->name !== 'langconfig') {
         $component->intersect($encomponent);
     } elseif ($version->code >= mlang_version::MOODLE_20) {
         if ($parentlanguage = $component->get_string('parentlanguage')) {
             if (substr($parentlanguage->text, -5) == '_utf8') {
                 $parentlanguage->text = substr($parentlanguage->text, 0, -5);
             }
         }
     }
     $stage->add($component);
Пример #18
0
 /**
  * Renders the stage
  *
  * @param local_amos_stage $stage
  * @return string
  */
 protected function render_local_amos_stage(local_amos_stage $stage)
 {
     global $CFG;
     $table = new html_table();
     $table->id = 'amosstage';
     $table->head = array(get_string('stagestring', 'local_amos'), get_string('stageoriginal', 'local_amos'), get_string('stagelang', 'local_amos'), get_string('stagetranslation', 'local_amos') . $this->help_icon('stagetranslation', 'local_amos'));
     $table->colclasses = array('stringinfo', 'original', 'lang', 'translation');
     $standard = array();
     foreach (local_amos_standard_plugins() as $plugins) {
         $standard = array_merge($standard, $plugins);
     }
     $committable = 0;
     foreach ($stage->strings as $string) {
         $cells = array();
         // string identification
         if (isset($standard[$string->component])) {
             $componentname = $standard[$string->component];
         } else {
             $componentname = $string->component;
         }
         $stringinfo = html_writer::tag('span', $string->version, array('class' => 'version'));
         $stringinfo .= '&nbsp;[' . html_writer::tag('span', $string->stringid, array('class' => 'stringid')) . ',' . html_writer::tag('span', $componentname, array('class' => 'component')) . ']';
         $cells[0] = new html_table_cell($stringinfo);
         // original of the string
         $cells[1] = new html_table_cell(html_writer::tag('div', self::add_breaks(s($string->original)), array('class' => 'preformatted')));
         // the language in which the original is displayed
         $cells[2] = new html_table_cell($string->language);
         // the current and the new translation
         $unstageurl = new moodle_url('/local/amos/stage.php', array('unstage' => $string->stringid, 'component' => $string->component, 'branch' => $string->branch, 'lang' => $string->language));
         $unstagebutton = $this->single_button($unstageurl, get_string('unstage', 'local_amos'), 'post', array('class' => 'singlebutton protected unstagebutton'));
         if ($string->deleted) {
             // removal of the string
             $t = self::add_breaks(s($string->current));
             $t = html_writer::tag('div', $t, array('class' => 'preformatted'));
             $cells[3] = new html_table_cell($t . $unstagebutton);
             if ($string->committable) {
                 $committable++;
                 $cells[3]->attributes['class'] .= 'committable removal';
             } else {
                 $cells[3]->attributes['class'] .= 'uncommittable removal';
             }
         } else {
             if (trim($string->current) === trim($string->new)) {
                 // no difference
                 $t = self::add_breaks(s($string->current));
                 $t = html_writer::tag('div', $t, array('class' => 'preformatted'));
                 $cells[3] = new html_table_cell($t . $unstagebutton);
                 $cells[3]->attributes['class'] .= ' uncommittable nodiff';
             } else {
                 if (is_null($string->current)) {
                     // new translation
                     $t = $t = self::add_breaks(s($string->new));
                     $t = html_writer::tag('div', $t, array('class' => 'preformatted'));
                     $cells[3] = new html_table_cell($t . $unstagebutton);
                     if ($string->committable) {
                         $cells[3]->attributes['class'] .= ' committable new';
                         $committable++;
                     }
                     if (!$string->committable) {
                         $cells[3]->attributes['class'] .= ' uncommittable new';
                     }
                 } else {
                     // there is a difference
                     $c = s($string->current);
                     $n = s($string->new);
                     $x1 = explode(' ', $c);
                     $x2 = explode(' ', $n);
                     $t = '';
                     $diff = local_amos_simplediff($x1, $x2);
                     $numd = 0;
                     $numi = 0;
                     foreach ($diff as $k) {
                         // $diff is a sequence of chunks (words) $k
                         if (is_array($k)) {
                             if (!empty($k['d'])) {
                                 $kd = implode(' ', $k['d']);
                                 if (!empty($kd)) {
                                     $t .= '<del>' . $kd . '</del> ';
                                     $numd += count($k['d']);
                                 }
                             }
                             if (!empty($k['i'])) {
                                 $ki = implode(' ', $k['i']);
                                 if (!empty($ki)) {
                                     $t .= '<ins>' . $ki . '</ins> ';
                                     $numi += count($k['i']);
                                 }
                             }
                         } else {
                             $t .= $k . ' ';
                         }
                     }
                     if ($numi == 0 or $numd == 0 or $numd == 1 and $numi == 1) {
                         $cstyle = 'display:none;';
                         $nstyle = 'display:none;';
                         $tstyle = 'display:block;';
                     } else {
                         $cstyle = 'display:block;';
                         $nstyle = 'display:block;';
                         $tstyle = 'display:none;';
                     }
                     $n = html_writer::tag('ins', $n);
                     $n = html_writer::tag('div', self::add_breaks($n), array('class' => 'preformatted stringtext new', 'style' => $nstyle));
                     $c = html_writer::tag('del', $c);
                     $c = html_writer::tag('div', self::add_breaks($c), array('class' => 'preformatted stringtext current', 'style' => $cstyle));
                     $t = html_writer::tag('div', self::add_breaks($t), array('class' => 'preformatted stringtext diff', 'style' => $tstyle));
                     $difflink = html_writer::tag('div', '', array('class' => 'diffmode'));
                     $cells[3] = new html_table_cell($difflink . $n . $c . $t . $unstagebutton);
                     if ($string->committable) {
                         $cells[3]->attributes['class'] .= ' committable diff';
                         $committable++;
                     }
                     if (!$string->committable) {
                         $cells[3]->attributes['class'] .= ' uncommittable diff';
                     }
                 }
             }
         }
         $row = new html_table_row($cells);
         $table->data[] = $row;
     }
     $table = html_writer::table($table);
     $propagateform = '';
     $propagateform .= html_writer::empty_tag('input', array('name' => 'sesskey', 'value' => sesskey(), 'type' => 'hidden'));
     $propagateform .= html_writer::empty_tag('input', array('name' => 'propagate', 'value' => 1, 'type' => 'hidden'));
     foreach (mlang_version::list_all() as $version) {
         if ($version->code < 2000) {
             continue;
         }
         $checkbox = html_writer::checkbox('ver[]', $version->code, true, $version->label);
         $propagateform .= html_writer::tag('div', $checkbox, array('class' => 'labelled_checkbox'));
     }
     $propagateform .= html_writer::empty_tag('input', array('value' => get_string('propagaterun', 'local_amos'), 'type' => 'submit'));
     $propagateform = html_writer::tag('div', $propagateform);
     $propagateform = html_writer::tag('form', $propagateform, array('method' => 'post', 'action' => $CFG->wwwroot . '/local/amos/stage.php'));
     $propagateform .= html_writer::tag('legend', get_string('propagate', 'local_amos') . $this->help_icon('propagate', 'local_amos'));
     $propagateform = html_writer::tag('fieldset', $propagateform, array('class' => 'propagateformwrapper protected'));
     $commitform = html_writer::label(get_string('commitmessage', 'local_amos'), 'commitmessage', false);
     $commitform .= html_writer::empty_tag('img', array('src' => $this->pix_url('req'), 'title' => 'Required', 'alt' => 'Required', 'class' => 'req'));
     $commitform .= html_writer::empty_tag('br');
     $commitform .= html_writer::tag('textarea', s($stage->presetmessage), array('id' => 'commitmessage', 'name' => 'message'));
     $commitform .= html_writer::empty_tag('input', array('name' => 'sesskey', 'value' => sesskey(), 'type' => 'hidden'));
     $button = html_writer::empty_tag('input', array('value' => get_string('commitbutton', 'local_amos'), 'type' => 'submit'));
     $button = html_writer::tag('div', $button);
     $commitform = html_writer::tag('div', $commitform . $button);
     $commitform = html_writer::tag('form', $commitform, array('method' => 'post', 'action' => $CFG->wwwroot . '/local/amos/stage.php'));
     $commitform .= html_writer::tag('legend', get_string('commitstage', 'local_amos') . $this->help_icon('commitstage', 'local_amos'));
     $commitform = html_writer::tag('fieldset', $commitform, array('class' => 'commitformwrapper protected'));
     $a = new stdClass();
     $a->time = userdate(time(), get_string('strftimedaydatetime', 'langconfig'));
     $stashtitle = get_string('stashtitledefault', 'local_amos', $a);
     $stashform = html_writer::label(get_string('stashtitle', 'local_amos'), 'stashtitle', true);
     $stashform .= html_writer::empty_tag('input', array('name' => 'sesskey', 'value' => sesskey(), 'type' => 'hidden'));
     $stashform .= html_writer::empty_tag('input', array('name' => 'new', 'value' => 1, 'type' => 'hidden'));
     $stashform .= html_writer::empty_tag('input', array('name' => 'name', 'value' => $stashtitle, 'type' => 'text', 'size' => 50, 'id' => 'stashtitle', 'maxlength' => 255));
     $stashform .= html_writer::empty_tag('input', array('value' => get_string('stashpush', 'local_amos'), 'type' => 'submit'));
     $stashform = html_writer::tag('div', $stashform);
     $stashform = html_writer::tag('form', $stashform, array('method' => 'post', 'action' => $CFG->wwwroot . '/local/amos/stash.php'));
     $stashform = html_writer::tag('div', $stashform, array('class' => 'stashformwrapper'));
     $submiturl = new moodle_url('/local/amos/stage.php', array('submit' => 1));
     $submitbutton = $this->single_button($submiturl, get_string('stagesubmit', 'local_amos'), 'post', array('class' => 'singlebutton submit'));
     $pruneurl = new moodle_url('/local/amos/stage.php', array('prune' => 1));
     $prunebutton = $this->single_button($pruneurl, get_string('stageprune', 'local_amos'), 'post', array('class' => 'singlebutton protected prune'));
     $rebaseurl = new moodle_url('/local/amos/stage.php', array('rebase' => 1));
     $rebasebutton = $this->single_button($rebaseurl, get_string('stagerebase', 'local_amos'), 'post', array('class' => 'singlebutton protected rebase'));
     $unstageallurl = new moodle_url('/local/amos/stage.php', array('unstageall' => 1));
     $unstageallbutton = $this->single_button($unstageallurl, get_string('stageunstageall', 'local_amos'), 'post', array('class' => 'singlebutton protected unstageall'));
     $i = 0;
     foreach ($stage->filterfields->fver as $fver) {
         $params['fver[' . $i . ']'] = $fver;
         $i++;
     }
     $i = 0;
     foreach ($stage->filterfields->flng as $flng) {
         $params['flng[' . $i . ']'] = $flng;
         $i++;
     }
     $i = 0;
     foreach ($stage->filterfields->fcmp as $fcmp) {
         $params['fcmp[' . $i . ']'] = $fcmp;
         $i++;
     }
     $params['fstg'] = 1;
     $params['__lazyform_amosfilter'] = 1;
     $editurl = new moodle_url('/local/amos/view.php', $params);
     $editbutton = $this->single_button($editurl, get_string('stageedit', 'local_amos'), 'post', array('class' => 'singlebutton edit'));
     if (empty($stage->strings)) {
         $output = $this->heading(get_string('stagestringsnone', 'local_amos'), 2, 'main', 'numberofstagedstrings');
         if ($stage->importform) {
             $legend = html_writer::tag('legend', get_string('importfile', 'local_amos') . $this->help_icon('importfile', 'local_amos'));
             ob_start();
             $stage->importform->display();
             $importform = ob_get_contents();
             ob_end_clean();
             $output .= html_writer::tag('fieldset', $legend . $importform, array('class' => 'wrappedmform importform'));
         }
         if ($stage->mergeform) {
             $legend = html_writer::tag('legend', get_string('mergestrings', 'local_amos') . $this->help_icon('mergestrings', 'local_amos'));
             ob_start();
             $stage->mergeform->display();
             $mergeform = ob_get_contents();
             ob_end_clean();
             $output .= html_writer::tag('fieldset', $legend . $mergeform, array('class' => 'wrappedmform mergeform'));
         }
         if ($stage->diffform) {
             $legend = html_writer::tag('legend', get_string('diffstrings', 'local_amos') . $this->help_icon('diffstrings', 'local_amos'));
             ob_start();
             $stage->diffform->display();
             $diffform = ob_get_contents();
             ob_end_clean();
             $output .= html_writer::tag('fieldset', $legend . $diffform, array('class' => 'wrappedmform diffform'));
         }
         if ($stage->executeform) {
             $legend = html_writer::tag('legend', get_string('script', 'local_amos') . $this->help_icon('script', 'local_amos'));
             ob_start();
             $stage->executeform->display();
             $executeform = ob_get_contents();
             ob_end_clean();
             $output .= html_writer::tag('fieldset', $legend . $executeform, array('class' => 'wrappedmform executeform'));
         }
     } else {
         $output = '';
         if (!empty($stage->stagedcontribution)) {
             $output .= $this->heading_with_help(get_string('contribstaged', 'local_amos', $stage->stagedcontribution), 'contribstagedinfo', 'local_amos');
         }
         $a = (object) array('staged' => count($stage->strings), 'committable' => $committable);
         if ($committable) {
             $output .= $this->heading(get_string('stagestringssome', 'local_amos', $a), 2, 'main', 'numberofstagedstrings');
         } else {
             $output .= $this->heading(get_string('stagestringsnocommit', 'local_amos', $a), 2, 'main', 'numberofstagedstrings');
         }
         unset($a);
         $justpropagated = optional_param('justpropagated', null, PARAM_INT);
         // usability hack to hide the propagator just after it was used
         if (is_null($justpropagated)) {
             $output .= $propagateform;
         } else {
             if ($justpropagated == 0) {
                 $output .= $this->heading(get_string('propagatednone', 'local_amos'));
             } else {
                 $output .= $this->heading(get_string('propagatedsome', 'local_amos', $justpropagated));
             }
         }
         if ($committable) {
             $output .= $commitform;
         }
         $legend = html_writer::tag('legend', get_string('stageactions', 'local_amos') . $this->help_icon('stageactions', 'local_amos'));
         $actionbuttons = $legend . $submitbutton . $editbutton;
         if ($committable) {
             $actionbuttons .= $prunebutton . $rebasebutton;
         }
         $actionbuttons .= $unstageallbutton;
         $output .= html_writer::tag('fieldset', $actionbuttons, array('class' => 'actionbuttons'));
         $legend = html_writer::tag('legend', get_string('stashactions', 'local_amos') . $this->help_icon('stashactions', 'local_amos'));
         $output .= html_writer::tag('fieldset', $legend . $stashform, array('class' => 'actionbuttons'));
         $output .= $table;
     }
     $output = html_writer::tag('div', $output, array('class' => 'stagewrapper'));
     return $output;
 }