MDL-21696 Added new admin report for language customization
authorDavid Mudrak <david.mudrak@gmail.com>
Tue, 24 Aug 2010 23:19:11 +0000 (23:19 +0000)
committerDavid Mudrak <david.mudrak@gmail.com>
Tue, 24 Aug 2010 23:19:11 +0000 (23:19 +0000)
12 files changed:
admin/report/customlang/db/access.php [new file with mode: 0644]
admin/report/customlang/db/install.xml [new file with mode: 0644]
admin/report/customlang/edit.php [new file with mode: 0644]
admin/report/customlang/filter_form.php [new file with mode: 0644]
admin/report/customlang/index.php [new file with mode: 0644]
admin/report/customlang/lang/en/report_customlang.php [new file with mode: 0644]
admin/report/customlang/lib.php [new file with mode: 0644]
admin/report/customlang/locallib.php [new file with mode: 0644]
admin/report/customlang/renderer.php [new file with mode: 0644]
admin/report/customlang/settings.php [new file with mode: 0644]
admin/report/customlang/styles.css [new file with mode: 0644]
admin/report/customlang/version.php [new file with mode: 0644]

diff --git a/admin/report/customlang/db/access.php b/admin/report/customlang/db/access.php
new file mode 100644 (file)
index 0000000..c8c2626
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Defines the capabilities used by the Language customization admin report
+ *
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$capabilities = array(
+
+    /* allows the user to view the current language customization */
+    'report/customlang:view' => array(
+        'riskbitmask' => RISK_CONFIG,
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'archetypes' => array(
+            'manager' => CAP_ALLOW
+        ),
+    ),
+
+    /* allows the user to edit the current language customization */
+    'report/customlang:edit' => array(
+        'riskbitmask' => RISK_CONFIG | RISK_XSS,
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'archetypes' => array(
+            'manager' => CAP_ALLOW
+        ),
+    ),
+
+);
diff --git a/admin/report/customlang/db/install.xml b/admin/report/customlang/db/install.xml
new file mode 100644 (file)
index 0000000..1366439
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<XMLDB PATH="admin/report/customlang/db" VERSION="20100825" COMMENT="XMLDB file for Moodle admin/report/customlang"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="../../../../lib/xmldb/xmldb.xsd"
+>
+  <TABLES>
+    <TABLE NAME="customlang" COMMENT="Contains the working checkout of all strings and their customization" NEXT="customlang_components">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="lang"/>
+        <FIELD NAME="lang" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" COMMENT="The code of the language this string belongs to. Like en, cs or es" PREVIOUS="id" NEXT="componentid"/>
+        <FIELD NAME="componentid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The id of the component" PREVIOUS="lang" NEXT="stringid"/>
+        <FIELD NAME="stringid" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The identifier of the string" PREVIOUS="componentid" NEXT="original"/>
+        <FIELD NAME="original" TYPE="text" LENGTH="big" NOTNULL="true" SEQUENCE="false" COMMENT="English original of the string" PREVIOUS="stringid" NEXT="master"/>
+        <FIELD NAME="master" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Master translation of the string as is distributed in the official lang pack, null if not translated" PREVIOUS="original" NEXT="local"/>
+        <FIELD NAME="local" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Local customization of the string, null if not customized" PREVIOUS="master" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="The timestamp of when the original or master was recently modified" PREVIOUS="local" NEXT="timecustomized"/>
+        <FIELD NAME="timecustomized" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="The timestamp of when the value of the local translation was recently modified, null if not customized yet" PREVIOUS="timemodified" NEXT="outdated"/>
+        <FIELD NAME="outdated" TYPE="int" LENGTH="3" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Either the English original or the master translation changed and the customization may be outdated" PREVIOUS="timecustomized" NEXT="modified"/>
+        <FIELD NAME="modified" TYPE="int" LENGTH="3" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Has the string been modified via the translator?" PREVIOUS="outdated"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="fk_component"/>
+        <KEY NAME="fk_component" TYPE="foreign" FIELDS="componentid" REFTABLE="customlang_components" REFFIELDS="id" PREVIOUS="primary"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="uq_lang_component_string" UNIQUE="true" FIELDS="lang, componentid, stringid" COMMENT="For a given language and component, string identifiers must be unique"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="customlang_components" COMMENT="Contains the list of all installed plugins that provide their own language pack" PREVIOUS="customlang">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="name"/>
+        <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The normalized name of the plugin" PREVIOUS="id" NEXT="version"/>
+        <FIELD NAME="version" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" COMMENT="The checked out version of the plugin, null of the version is unknown" PREVIOUS="name"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+      </KEYS>
+    </TABLE>
+  </TABLES>
+</XMLDB>
\ No newline at end of file
diff --git a/admin/report/customlang/edit.php b/admin/report/customlang/edit.php
new file mode 100644 (file)
index 0000000..56e2595
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
+require_once($CFG->dirroot.'/'.$CFG->admin.'/report/customlang/locallib.php');
+require_once($CFG->dirroot.'/'.$CFG->admin.'/report/customlang/filter_form.php');
+
+require_login(SITEID, false);
+require_capability('report/customlang:edit', get_system_context());
+
+$currentpage            = optional_param('p', 0, PARAM_INT);
+$translatorsubmitted    = optional_param('translatorsubmitted', 0, PARAM_BOOL);
+
+$PAGE->set_pagelayout('standard');
+$PAGE->set_url('/admin/report/customlang/edit.php');
+navigation_node::override_active_url(new moodle_url('/admin/report/customlang/index.php'));
+$PAGE->set_title(get_string('pluginname', 'report_customlang'));
+$PAGE->set_heading(get_string('pluginname', 'report_customlang'));
+//$PAGE->requires->js_init_call('M.report_customlang.init_translator', array(), true);
+
+// pre-output processing
+$filter     = new report_customlang_filter_form($PAGE->url, null, 'post', '', array('class'=>'filterform'));
+$filterdata = report_customlang_utils::load_filter($USER);
+$filter->set_data($filterdata);
+
+if ($filter->is_cancelled()) {
+    redirect($PAGE->url);
+
+} elseif ($submitted = $filter->get_data()) {
+    report_customlang_utils::save_filter($submitted, $USER);
+    redirect(new moodle_url($PAGE->url, array('p'=>0)));
+}
+
+if ($translatorsubmitted) {
+    $strings = optional_param('cust', array(), PARAM_RAW);
+    $updates = optional_param('updates', array(), PARAM_INT);
+    $checkin = optional_param('savecheckin', false, PARAM_RAW);
+
+    if ($checkin === false) {
+        $nexturl = $PAGE->url;
+    } else {
+        $nexturl = new moodle_url('/admin/report/customlang/index.php', array('action'=>'checkin', 'sesskey'=>sesskey()));
+    }
+
+    if (!is_array($strings)) {
+        $strings = array();
+    }
+    $current = $DB->get_records_list('customlang', 'id', array_keys($strings));
+    $now = time();
+
+    foreach ($strings as $recordid => $customization) {
+        $customization = trim($customization);
+
+        if (empty($customization) and !is_null($current[$recordid]->local)) {
+            $current[$recordid]->local = null;
+            $current[$recordid]->modified = 1;
+            $current[$recordid]->outdated = 0;
+            $current[$recordid]->timecustomized = null;
+            $DB->update_record('customlang', $current[$recordid]);
+            continue;
+        }
+
+        if (empty($customization)) {
+            continue;
+        }
+
+        if ($customization !== $current[$recordid]->local) {
+            $current[$recordid]->local = $customization;
+            $current[$recordid]->modified = 1;
+            $current[$recordid]->outdated = 0;
+            $current[$recordid]->timecustomized = $now;
+            $DB->update_record('customlang', $current[$recordid]);
+            continue;
+        }
+    }
+
+    if (!is_array($updates)) {
+        $updates = array();
+    }
+    if (!empty($updates)) {
+        list($sql, $params) = $DB->get_in_or_equal($updates);
+        $DB->set_field_select('customlang', 'outdated', 0, "local IS NOT NULL AND id $sql", $params);
+    }
+
+    redirect($nexturl);
+}
+
+$translator = new report_customlang_translator($PAGE->url, current_language(), $filterdata, $currentpage);
+
+// output starts here
+$output     = $PAGE->get_renderer('report_customlang');
+$paginator  = $output->paging_bar($translator->numofrows, $currentpage, report_customlang_translator::PERPAGE, $PAGE->url, 'p');
+
+echo $output->header();
+$filter->display();
+echo $paginator;
+echo $output->render($translator);
+echo $paginator;
+echo $output->footer();
diff --git a/admin/report/customlang/filter_form.php b/admin/report/customlang/filter_form.php
new file mode 100644 (file)
index 0000000..8acba6b
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/lib/formslib.php');
+
+/**
+ * Form for filtering the strings to customize
+ */
+class report_customlang_filter_form extends moodleform {
+
+    function definition() {
+        $mform = $this->_form;
+        $current = $this->_customdata['current'];
+
+        $mform->addElement('header', 'filtersettings', get_string('filter', 'report_customlang'));
+
+        // Component
+        $options = array();
+        foreach (report_customlang_utils::list_components() as $component => $normalized) {
+            list($type, $plugin) = normalize_component($normalized);
+            if ($type == 'core' and is_null($plugin)) {
+                $plugin = 'moodle';
+            }
+            $options[$type][$normalized] = $component.'.php';
+        }
+        $mform->addElement('selectgroups', 'component', get_string('filtercomponent', 'report_customlang'), $options,
+                           array('multiple'=>'multiple', 'size'=>7));
+
+        // Customized only
+        $mform->addElement('advcheckbox', 'customized', get_string('filtercustomized', 'report_customlang'));
+        $mform->setType('customized', PARAM_BOOL);
+        $mform->setDefault('customized', 0);
+
+        // Only helps
+        $mform->addElement('advcheckbox', 'helps', get_string('filteronlyhelps', 'report_customlang'));
+        $mform->setType('helps', PARAM_BOOL);
+        $mform->setDefault('helps', 0);
+
+        // Modified only
+        $mform->addElement('advcheckbox', 'modified', get_string('filtermodified', 'report_customlang'));
+        $mform->setType('filtermodified', PARAM_BOOL);
+        $mform->setDefault('filtermodified', 0);
+
+        // Substring
+        $mform->addElement('text', 'substring', get_string('filtersubstring', 'report_customlang'));
+        $mform->setType('substring', PARAM_RAW);
+
+        // String identifier
+        $mform->addElement('text', 'stringid', get_string('filterstringid', 'report_customlang'));
+        $mform->setType('stringid', PARAM_STRINGID);
+
+        // Show strings - submit button
+        $mform->addElement('submit', 'submit', get_string('filtershowstrings', 'report_customlang'));
+    }
+}
+
diff --git a/admin/report/customlang/index.php b/admin/report/customlang/index.php
new file mode 100644 (file)
index 0000000..e6bd24d
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Performs checkout of the strings into the translation table
+ *
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
+require_once($CFG->dirroot.'/'.$CFG->admin.'/report/customlang/locallib.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+require_login(SITEID, false);
+require_capability('report/customlang:view', get_system_context());
+
+$action  = optional_param('action', '', PARAM_ALPHA);
+$confirm = optional_param('confirm', false, PARAM_BOOL);
+
+admin_externalpage_setup('reportcustomlang');
+
+// pre-output actions
+if ($action === 'checkout') {
+    require_sesskey();
+    require_capability('report/customlang:edit', get_system_context());
+    report_customlang_utils::checkout(current_language());
+    redirect(new moodle_url('/admin/report/customlang/edit.php'));
+}
+
+if ($action === 'checkin') {
+    require_sesskey();
+    require_capability('report/customlang:edit', get_system_context());
+
+    if (!$confirm) {
+        $output = $PAGE->get_renderer('report_customlang');
+        echo $output->header();
+        echo $output->heading(get_string('pluginname', 'report_customlang'));
+        $numofmodified = report_customlang_utils::get_count_of_modified(current_language());
+        if ($numofmodified != 0) {
+            echo $output->heading(get_string('modifiednum', 'report_customlang', $numofmodified), 3);
+            echo $output->confirm(get_string('confirmcheckin', 'report_customlang'),
+                                  new moodle_url($PAGE->url, array('action'=>'checkin', 'confirm'=>1)), $PAGE->url);
+        } else {
+            echo $output->heading(get_string('modifiedno', 'report_customlang', $numofmodified), 3);
+            echo $output->continue_button($PAGE->url);
+        }
+        echo $output->footer();
+        die();
+
+    } else {
+        report_customlang_utils::checkin(current_language());
+        redirect($PAGE->url);
+    }
+}
+
+$output = $PAGE->get_renderer('report_customlang');
+
+// output starts here
+echo $output->header();
+echo $output->heading(get_string('pluginname', 'report_customlang'));
+
+echo $output->box($output->lang_menu(), 'langmenubox');
+
+$numofmodified = report_customlang_utils::get_count_of_modified(current_language());
+
+if ($numofmodified != 0) {
+    echo $output->heading(get_string('modifiednum', 'report_customlang', $numofmodified), 3);
+}
+
+$menu = array();
+if (has_capability('report/customlang:edit', get_system_context())) {
+    $menu['checkout'] = array(
+        'title'     => get_string('checkout', 'report_customlang'),
+        'url'       => new moodle_url($PAGE->url, array('action' => 'checkout')),
+        'method'    => 'post',
+    );
+    if ($numofmodified != 0) {
+        $menu['checkin'] = array(
+            'title'     => get_string('checkin', 'report_customlang'),
+            'url'       => new moodle_url($PAGE->url, array('action' => 'checkin')),
+            'method'    => 'post',
+        );
+    }
+}
+echo $output->render(new report_customlang_menu($menu));
+
+echo $output->footer();
diff --git a/admin/report/customlang/lang/en/report_customlang.php b/admin/report/customlang/lang/en/report_customlang.php
new file mode 100644 (file)
index 0000000..4d3b7ad
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Strings for Language customization admin report
+ *
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['checkin'] = 'Check in strings into disk';
+$string['checkout'] = 'Check out strings into translator';
+$string['confirmcheckin'] = 'You are about to check in modified strings into your local language pack. This will export the customized strings from the translator into the data directory and Moodle will start using the modified strings. Press \'Continue\' button to proceed check in.';
+$string['customlang:edit'] = 'Edit local translation';
+$string['customlang:view'] = 'View local translation';
+$string['filter'] = 'Filter strings';
+$string['filtercomponent'] = 'Show strings of these components';
+$string['filtercustomized'] = 'Customized only';
+$string['filtermodified'] = 'Modified only';
+$string['filteronlyhelps'] = 'Help only';
+$string['filtershowstrings'] = 'Show strings';
+$string['filterstringid'] = 'String identifier';
+$string['filtersubstring'] = 'Only strings containing';
+$string['headingcomponent'] = 'Component';
+$string['headinglocal'] = 'Local customization';
+$string['headingstandard'] = 'Standard text';
+$string['headingstringid'] = 'String';
+$string['markinguptodate'] = 'Marking the customization as up-to-date';
+$string['markinguptodate_help'] = 'The customized translation may get outdated if either the English original or the master translation has modified since the string was customized on your site. Review the customized translation. If you find it up-to-date, click the checkbox. Edit it otherwise.';
+$string['markuptodate'] = 'mark as up-to-date';
+$string['modifiedno'] = 'There are no modified strings to check in.';
+$string['modifiednum'] = 'There are {$a} modified strings. You must check in them into disk to store them permanently.';
+$string['nostringsfound'] = 'No strings found, please modify the filter settings';
+$string['pluginname'] = 'Language customization';
+$string['savecheckin'] = 'Save and check in strings into files';
+$string['savecontinue'] = 'Save and continue editing';
diff --git a/admin/report/customlang/lib.php b/admin/report/customlang/lib.php
new file mode 100644 (file)
index 0000000..d3a1fb1
--- /dev/null
@@ -0,0 +1,25 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
diff --git a/admin/report/customlang/locallib.php b/admin/report/customlang/locallib.php
new file mode 100644 (file)
index 0000000..c856980
--- /dev/null
@@ -0,0 +1,511 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Definition of classes used by Langugae customization admin report
+ *
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Provides various utilities to be used by the plugin
+ *
+ * All the public methods here are static ones, this class can not be instantiated
+ */
+class report_customlang_utils {
+
+    /** @var array cache of {@link self::list_components()} results */
+    protected static $components = null;
+
+    /**
+     * This class can not be instantiated
+     */
+    private function __construct() {
+    }
+
+    /**
+     * Returns a list of all components installed on the server
+     *
+     * @return array (string)legacyname => (string)frankenstylename
+     */
+    public static function list_components() {
+
+        $list['moodle'] = 'core';
+
+        $coresubsystems = get_core_subsystems();
+        ksort($coresubsystems); // should be but just in case
+        foreach ($coresubsystems as $name => $location) {
+            if ($name != 'moodle.org') {
+                $list[$name] = 'core_'.$name;
+            }
+        }
+
+        $plugintypes = get_plugin_types();
+        foreach ($plugintypes as $type => $location) {
+            $pluginlist = get_plugin_list($type);
+            foreach ($pluginlist as $name => $ununsed) {
+                if ($type == 'mod') {
+                    if (array_key_exists($name, $list)) {
+                        throw new Exception('Activity module and core subsystem name collision');
+                    }
+                    $list[$name] = $type.'_'.$name;
+                } else {
+                    $list[$type.'_'.$name] = $type.'_'.$name;
+                }
+            }
+        }
+
+        return $list;
+    }
+
+    /**
+     * Updates the translator database with the strings from files
+     *
+     * This should be executed each time before going to the translation page
+     *
+     * @param string $lang language code to checkout
+     */
+    public static function checkout($lang) {
+        global $DB;
+
+        // make sure that all components are registered
+        $current = $DB->get_records('customlang_components', null, 'name', 'name,version,id');
+        foreach (self::list_components() as $component) {
+            if (empty($current[$component])) {
+                $record = new stdclass();
+                $record->name = $component;
+                if (!$version = get_component_version($component)) {
+                    $record->version = null;
+                } else {
+                    $record->version = $version;
+                }
+                $DB->insert_record('customlang_components', $record);
+            } elseif ($version = get_component_version($component)) {
+                if (is_null($current[$component]->version) or ($version > $current[$component]->version)) {
+                    $DB->set_field('customlang_components', 'version', $version, array('id' => $current[$component]->id));
+                }
+            }
+        }
+        unset($current);
+
+        // reload components and fetch their strings
+        $stringman  = get_string_manager();
+        $components = $DB->get_records('customlang_components');
+        foreach ($components as $component) {
+            $current = $DB->get_records('customlang', array('lang'=>$lang, 'componentid'=>$component->id), 'stringid', 'stringid, *');
+            $english = $stringman->load_component_strings($component->name, 'en', true, true);
+            if ($lang == 'en') {
+                $master =& $english;
+            } else {
+                $master = $stringman->load_component_strings($component->name, $lang, true, true);
+            }
+            $local = $stringman->load_component_strings($component->name, $lang, true, false);
+
+            foreach ($english as $stringid => $stringoriginal) {
+                $stringmaster = isset($master[$stringid]) ? $master[$stringid] : null;
+                $stringlocal = isset($local[$stringid]) ? $local[$stringid] : null;
+                $now = time();
+
+                if (isset($current[$stringid])) {
+                    $needsupdate     = false;
+                    $currentoriginal = $current[$stringid]->original;
+                    $currentmaster   = $current[$stringid]->master;
+                    $currentlocal    = $current[$stringid]->local;
+
+                    if ($currentoriginal !== $stringoriginal or $currentmaster !== $stringmaster) {
+                        $needsupdate = true;
+                        $current[$stringid]->original       = $stringoriginal;
+                        $current[$stringid]->master         = $stringmaster;
+                        $current[$stringid]->timemodified   = $now;
+                        $current[$stringid]->outdated       = 1;
+                    }
+
+                    if ($stringmaster !== $stringlocal) {
+                        $needsupdate = true;
+                        $current[$stringid]->local          = $stringlocal;
+                        $current[$stringid]->timecustomized = $now;
+                    }
+
+                    if ($needsupdate) {
+                        $DB->update_record('customlang', $current[$stringid]);
+                        continue;
+                    }
+
+                } else {
+                    $record                 = new stdclass();
+                    $record->lang           = $lang;
+                    $record->componentid    = $component->id;
+                    $record->stringid       = $stringid;
+                    $record->original       = $stringoriginal;
+                    $record->master         = $stringmaster;
+                    $record->timemodified   = $now;
+                    $record->outdated       = 0;
+                    if ($stringmaster !== $stringlocal) {
+                        $record->local          = $stringlocal;
+                        $record->timecustomized = $now;
+                    } else {
+                        $record->local          = null;
+                        $record->timecustomized = null;
+                    }
+
+                    $DB->insert_record('customlang', $record);
+                }
+            }
+        }
+    }
+
+    /**
+     * Exports the translator database into disk files
+     *
+     * @param mixed $lang language code
+     */
+    public static function checkin($lang) {
+        global $DB, $USER, $CFG;
+        require_once($CFG->libdir.'/filelib.php');
+
+        if ($lang !== clean_param($lang, PARAM_LANG)) {
+            return false;
+        }
+
+        // get all customized strings from updated components
+        $sql = "SELECT s.*, c.name AS component
+                  FROM {customlang} s
+                  JOIN {customlang_components} c ON s.componentid = c.id
+                 WHERE s.lang = ?
+                       AND (s.local IS NOT NULL OR s.modified = 1)
+              ORDER BY componentid, stringid";
+        $strings = $DB->get_records_sql($sql, array($lang));
+
+        $files = array();
+        foreach ($strings as $string) {
+            if (!is_null($string->local)) {
+                $files[$string->component][$string->stringid] = $string->local;
+            }
+        }
+
+        fulldelete(self::get_localpack_location($lang));
+        foreach ($files as $component => $strings) {
+            self::dump_strings($lang, $component, $strings);
+        }
+
+        $DB->set_field_select('customlang', 'modified', 0, 'lang = ?', array($lang));
+        $sm = get_string_manager();
+        $sm->reset_caches();
+    }
+
+    /**
+     * Returns full path to the directory where local packs are dumped into
+     *
+     * @param string $lang language code
+     * @return string full path
+     */
+    protected static function get_localpack_location($lang) {
+        global $CFG;
+
+        return $CFG->langlocalroot.'/'.$lang.'_local';
+    }
+
+    /**
+     * Writes strings into a local language pack file
+     *
+     * @param string $component the name of the component
+     * @param array $strings
+     */
+    protected static function dump_strings($lang, $component, $strings) {
+        global $CFG;
+
+        if ($lang !== clean_param($lang, PARAM_LANG)) {
+            debugging('Unable to dump local strings for non-installed language pack .'.s($lang));
+            return false;
+        }
+        if ($component !== clean_param($component, PARAM_SAFEDIR)) {
+            throw new coding_exception('Incorrect component name');
+        }
+        if (!$filename = self::get_component_filename($component)) {
+            debugging('Unable to find the filename for the component '.s($component));
+            return false;
+        }
+        if ($filename !== clean_param($filename, PARAM_FILE)) {
+            throw new coding_exception('Incorrect file name '.s($filename));
+        }
+        list($package, $subpackage) = normalize_component($component);
+        $packageinfo = " * @package    $package";
+        if (!is_null($subpackage)) {
+            $packageinfo .= "\n * @subpackage $subpackage";
+        }
+        $filepath = self::get_localpack_location($lang);
+        if ($filepath !== clean_param($filepath, PARAM_SAFEPATH)) {
+            throw new coding_exception('Incorrect file location '.s($filepath));
+        }
+        $filepath = $filepath.'/'.$filename;
+        if (!is_dir(dirname($filepath))) {
+            mkdir(dirname($filepath), 0755, true);
+        }
+
+        if (!$f = fopen($filepath, 'w')) {
+            debugging('Unable to write '.s($filepath));
+            return false;
+        }
+        fwrite($f, <<<EOF
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Local language pack from $CFG->wwwroot
+ *
+$packageinfo
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+EOF
+        );
+
+        foreach ($strings as $stringid => $text) {
+            if ($stringid !== clean_param($stringid, PARAM_STRINGID)) {
+                debugging('Invalid string identifier '.s($stringid));
+                continue;
+            }
+            fwrite($f, '$string[\'' . $stringid . '\'] = ');
+            fwrite($f, var_export($text, true));
+            fwrite($f, ";\n");
+        }
+        fclose($f);
+    }
+
+    /**
+     * Returns the name of the file where the component's local strings should be exported into
+     *
+     * @param string $component normalized name of the component, eg 'core' or 'mod_workshop'
+     * @return string|boolean filename eg 'moodle.php' or 'workshop.php', false if not found
+     */
+    protected static function get_component_filename($component) {
+        if (is_null(self::$components)) {
+            self::$components = self::list_components();
+        }
+        $return = false;
+        foreach (self::$components as $legacy => $normalized) {
+            if ($component === $normalized) {
+                $return = $legacy.'.php';
+                break;
+            }
+        }
+        return $return;
+    }
+
+    /**
+     * Returns the number of modified strings checked out in the translator
+     *
+     * @param string $lang language code
+     * @return int
+     */
+    public static function get_count_of_modified($lang) {
+        global $DB;
+
+        return $DB->count_records('customlang', array('lang'=>$lang, 'modified'=>1));
+    }
+
+    /**
+     * Saves filter data into a persistant storage such as user session
+     *
+     * @see self::load_filter()
+     * @param stdclass $data filter values
+     * @param stdclass $persistant storage object
+     */
+    public static function save_filter(stdclass $data, stdclass $persistant) {
+        if (!isset($persistant->report_customlang_filter)) {
+            $persistant->report_customlang_filter = array();
+        }
+        foreach ($data as $key => $value) {
+            if ($key !== 'submit') {
+                $persistant->report_customlang_filter[$key] = serialize($value);
+            }
+        }
+    }
+
+    /**
+     * Loads the previously saved filter settings from a persistent storage
+     *
+     * @see self::save_filter()
+     * @param stdclass $persistant storage object
+     * @return stdclass filter data
+     */
+    public static function load_filter(stdclass $persistant) {
+        $data = new stdclass();
+        if (isset($persistant->report_customlang_filter)) {
+            foreach ($persistant->report_customlang_filter as $key => $value) {
+                $data->{$key} = unserialize($value);
+            }
+        }
+        return $data;
+    }
+}
+
+/**
+ * Represents the action menu of the report
+ */
+class report_customlang_menu implements renderable {
+
+    /** @var menu items */
+    protected $items = array();
+
+    public function __construct(array $items = array()) {
+        global $CFG;
+
+        foreach ($items as $itemkey => $item) {
+            $this->add_item($itemkey, $item['title'], $item['url'], empty($item['method']) ? 'post' : $item['method']);
+        }
+    }
+
+    /**
+     * Returns the menu items
+     *
+     * @return array (string)key => (object)[->(string)title ->(moodl_url)url ->(string)method]
+     */
+    public function get_items() {
+        return $this->items;
+    }
+
+    /**
+     * Adds item into the menu
+     *
+     * @param string $key item identifier
+     * @param string $title localized action title
+     * @param moodle_url $url action handler
+     * @param string $method form method
+     */
+    public function add_item($key, $title, moodle_url $url, $method) {
+        if (isset($this->items[$key])) {
+            throw new coding_error('Menu item already exists');
+        }
+        if (empty($title) or empty($key)) {
+            throw new coding_error('Empty title or item key not allowed');
+        }
+        $item = new stdclass();
+        $item->title = $title;
+        $item->url = $url;
+        $item->method = $method;
+        $this->items[$key] = $item;
+    }
+}
+
+/**
+ * Represents the translation tool
+ */
+class report_customlang_translator implements renderable {
+
+    /** @const int number of rows per page */
+    const PERPAGE = 100;
+
+    /** @var int total number of the rows int the table */
+    public $numofrows = 0;
+
+    /** @var moodle_url */
+    public $handler;
+
+    /** @var string language code */
+    public $lang;
+
+    /** @var int page to display, starting with page 0 */
+    public $currentpage = 0;
+
+    /** @var array of stdclass strings to display */
+    public $strings = array();
+
+    /** @var stdclass */
+    protected $filter;
+
+    public function __construct(moodle_url $handler, $lang, $filter, $currentpage = 0) {
+        global $DB;
+
+        $this->handler      = $handler;
+        $this->lang         = $lang;
+        $this->filter       = $filter;
+        $this->currentpage  = $currentpage;
+
+        if (empty($filter) or empty($filter->component)) {
+            // nothing to do
+            $this->currentpage = 1;
+            return;
+        }
+
+        list($insql, $inparams) = $DB->get_in_or_equal($filter->component, SQL_PARAMS_NAMED);
+
+        $csql = "SELECT COUNT(*)";
+        $fsql = "SELECT s.id, s.*, c.name AS component";
+        $sql  = "  FROM {customlang_components} c
+                   JOIN {customlang} s ON s.componentid = c.id
+                  WHERE s.lang = :lang
+                        AND c.name $insql";
+
+        $params = array_merge(array('lang' => $lang), $inparams);
+
+        if (!empty($filter->customized)) {
+            $sql .= "   AND s.local IS NOT NULL";
+        }
+
+        if (!empty($filter->modified)) {
+            $sql .= "   AND s.modified = 1";
+        }
+
+        if (!empty($filter->stringid)) {
+            $sql .= "   AND s.stringid = :stringid";
+            $params['stringid'] = $filter->stringid;
+        }
+
+        if (!empty($filter->substring)) {
+            $sql .= "   AND (s.original ".$DB->sql_ilike()." :substringoriginal OR
+                             s.master ".$DB->sql_ilike()." :substringmaster OR
+                             s.local ".$DB->sql_ilike()." :substringlocal)";
+            $params['substringoriginal'] = '%'.$filter->substring.'%';
+            $params['substringmaster']   = '%'.$filter->substring.'%';
+            $params['substringlocal']    = '%'.$filter->substring.'%';
+        }
+
+        if (!empty($filter->helps)) {
+            $sql .= "   AND s.stringid ".$DB->sql_ilike()." '%\\\\_help'";
+        } else {
+            $sql .= "   AND s.stringid NOT ".$DB->sql_ilike()." '%\\\\_link'";
+        }
+
+        $osql = " ORDER BY c.name, s.stringid";
+
+        $this->numofrows = $DB->count_records_sql($csql.$sql, $params);
+        $this->strings = $DB->get_records_sql($fsql.$sql.$osql, $params, ($this->currentpage) * self::PERPAGE, self::PERPAGE);
+    }
+}
diff --git a/admin/report/customlang/renderer.php b/admin/report/customlang/renderer.php
new file mode 100644 (file)
index 0000000..4304e38
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Output rendering of Language customization admin report
+ *
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Rendering methods for the report widgets
+ */
+class report_customlang_renderer extends plugin_renderer_base {
+
+    /**
+     * Renders customlang report menu
+     *
+     * @return string HTML
+     */
+    protected function render_report_customlang_menu(report_customlang_menu $menu) {
+        $output = '';
+        foreach ($menu->get_items() as $item) {
+            $output .= $this->single_button($item->url, $item->title, $item->method);
+        }
+        return $this->box($output, 'menu');
+    }
+
+    /**
+     * Renders customlang translation table
+     *
+     * @param report_customlang_translator $translator
+     * @return string HTML
+     */
+    protected function render_report_customlang_translator(report_customlang_translator $translator) {
+        $output = '';
+
+        if (empty($translator->strings)) {
+            return $this->heading(get_string('nostringsfound', 'report_customlang'), 3);
+        }
+
+        $table = new html_table();
+        $table->id = 'translator';
+        $table->head = array(
+            get_string('headingcomponent', 'report_customlang'),
+            get_string('headingstringid', 'report_customlang'),
+            get_string('headingstandard', 'report_customlang'),
+            get_string('headinglocal', 'report_customlang'),
+        );
+
+        foreach ($translator->strings as $string) {
+            $cells = array();
+            // component name
+            $cells[0] = new html_table_cell($string->component);
+            $cells[0]->attributes['class'] = 'component';
+            // string identification code
+            $cells[1] = new html_table_cell(html_writer::tag('div', s($string->stringid), array('class' => 'stringid')));
+            $cells[1]->attributes['class'] = 'stringid';
+            // master translation of the string
+            $cells[2] = new html_table_cell(html_writer::tag('div', s($string->master), array('class' => 'preformatted')));
+            $cells[2]->attributes['class'] = 'standard master';
+            // local customization of the string
+            $textarea = html_writer::tag('textarea', s($string->local), array('name'=>'cust['.$string->id.']'));
+            $cells[3] = new html_table_cell($textarea);
+            if (!is_null($string->local) and $string->outdated) {
+                $mark  = html_writer::empty_tag('input', array('type' => 'checkbox', 'id' => 'update_' . $string->id,
+                                                               'name' => 'updates[]', 'value' => $string->id));
+                $help  = $this->help_icon('markinguptodate', 'report_customlang');
+                $mark .= html_writer::tag('label', get_string('markuptodate', 'report_customlang') . $help,
+                                          array('for' => 'update_' . $string->id));
+                $mark  = html_writer::tag('div', $mark, array('class' => 'uptodatewrapper'));
+            } else {
+                $mark  = '';
+            }
+            $cells[3] = new html_table_cell($textarea."\n".$mark);
+            $cells[3]->attributes['class'] = 'local';
+            $cells[3]->id = 'id_'.$string->id;
+            if (!is_null($string->local)) {
+                $cells[3]->attributes['class'] .= ' customized';
+            }
+            if ($string->outdated) {
+                $cells[3]->attributes['class'] .= ' outdated';
+            }
+            if ($string->modified) {
+                $cells[3]->attributes['class'] .= ' modified';
+            }
+
+            if ($string->original !== $string->master) {
+                $cells[0]->rowspan = $cells[1]->rowspan = $cells[3]->rowspan = 2;
+            }
+
+            $row = new html_table_row($cells);
+            $table->data[] = $row;
+
+            if ($string->original !== $string->master) {
+                $cells = array();
+                // original of the string
+                $cells[2] = new html_table_cell(html_writer::tag('div', s($string->original), array('class' => 'preformatted')));
+                $cells[2]->attributes['class'] = 'standard original';
+                $row = new html_table_row($cells);
+                $table->data[] = $row;
+            }
+        }
+
+        $output .= html_writer::start_tag('form', array('method'=>'post', 'action'=>$translator->handler->out()));
+        $output .= html_writer::start_tag('div');
+        $output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'translatorsubmitted', 'value'=>1));
+        $output .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
+        $save1   = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savecontinue', 'value'=>get_string('savecontinue', 'report_customlang')));
+        $save2   = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'savecheckin', 'value'=>get_string('savecheckin', 'report_customlang')));
+        $output .= html_writer::tag('fieldset', $save1.$save2, array('class'=>'buttonsbar'));
+        $output .= html_writer::table($table);
+        $output .= html_writer::tag('fieldset', $save1.$save2, array('class'=>'buttonsbar'));
+        $output .= html_writer::end_tag('div');
+        $output .= html_writer::end_tag('form');
+
+        return $output;
+    }
+}
diff --git a/admin/report/customlang/settings.php b/admin/report/customlang/settings.php
new file mode 100644 (file)
index 0000000..97564e7
--- /dev/null
@@ -0,0 +1,27 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$ADMIN->add('language', new admin_externalpage('reportcustomlang', get_string('pluginname', 'report_customlang'), "$CFG->wwwroot/$CFG->admin/report/customlang/index.php", 'report/customlang:view'));
diff --git a/admin/report/customlang/styles.css b/admin/report/customlang/styles.css
new file mode 100644 (file)
index 0000000..a7e7a4a
--- /dev/null
@@ -0,0 +1,58 @@
+.path-admin-report-customlang .langmenubox,
+.path-admin-report-customlang fieldset.buttonsbar,
+.path-admin-report-customlang .menu {
+    margin: 5px auto;
+    text-align: center;
+}
+
+.path-admin-report-customlang .menu .singlebutton,
+.path-admin-report-customlang .menu .singlebutton form,
+.path-admin-report-customlang .menu .singlebutton form div {
+    display: inline;
+}
+
+
+.path-admin-report-customlang .mform.filterform {
+    width: 70%;
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.path-admin-report-customlang .mform.filterform .fitem .fitemtitle {
+    width: 30%;
+}
+
+.path-admin-report-customlang .mform.filterform .fitem .felement {
+    width: 60%;
+    margin-left: 31%;
+}
+
+.path-admin-report-customlang #translator {
+    width: 100%;
+}
+
+.path-admin-report-customlang #translator .standard,
+.path-admin-report-customlang #translator .local {
+    min-width: 35%;
+}
+
+.path-admin-report-customlang #translator .customized {
+    background-color: #e7f1c3;
+}
+
+.path-admin-report-customlang #translator .customized.outdated {
+    background-color: #f3f2aa;
+}
+
+.path-admin-report-customlang #translator .modified {
+    background-color: #ffd3d9;
+}
+
+.path-admin-report-customlang #translator .customized.modified {
+    background-color: #d2ebff;
+}
+
+.path-admin-report-customlang #translator textarea {
+    width: 100%;
+    min-height: 4em;
+}
diff --git a/admin/report/customlang/version.php b/admin/report/customlang/version.php
new file mode 100644 (file)
index 0000000..150306d
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * @package    report
+ * @subpackage customlang
+ * @copyright  2010 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$plugin->version  = 2010082500;
+$plugin->requires = 2010080901;