Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
/**
18
 * Entry caching for glossary filter.
19
 *
20
 * @package    mod_glossary
21
 * @copyright  2014 Petr Skoda
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace mod_glossary\local;
26
defined('MOODLE_INTERNAL') || die();
27
 
28
/**
29
 * Concept caching for glossary filter.
30
 *
31
 * @package    mod_glossary
32
 * @copyright  2014 Petr Skoda
33
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
34
 */
35
class concept_cache {
36
    /**
37
     * Event observer, do not call directly.
38
     * @param \core\event\course_module_updated $event
39
     */
40
    public static function cm_updated(\core\event\course_module_updated $event) {
41
        if ($event->other['modulename'] !== 'glossary') {
42
            return;
43
        }
44
        // We do not know what changed exactly, so let's reset everything that might be affected.
45
        concept_cache::reset_course_muc($event->courseid);
46
        concept_cache::reset_global_muc();
47
    }
48
 
49
    /**
50
     * Reset concept related caches.
51
     * @param bool $phpunitreset
52
     */
53
    public static function reset_caches($phpunitreset = false) {
54
        if ($phpunitreset) {
55
            return;
56
        }
57
        $cache = \cache::make('mod_glossary', 'concepts');
58
        $cache->purge();
59
    }
60
 
61
    /**
62
     * Reset the cache for course concepts.
63
     * @param int $courseid
64
     */
65
    public static function reset_course_muc($courseid) {
66
        if (empty($courseid)) {
67
            return;
68
        }
69
        $cache = \cache::make('mod_glossary', 'concepts');
70
        $cache->delete((int)$courseid);
71
    }
72
 
73
    /**
74
     * Reset the cache for global concepts.
75
     */
76
    public static function reset_global_muc() {
77
        $cache = \cache::make('mod_glossary', 'concepts');
78
        $cache->delete(0);
79
    }
80
 
81
    /**
82
     * Utility method to purge caches related to given glossary.
83
     * @param \stdClass $glossary
84
     */
85
    public static function reset_glossary($glossary) {
86
        if (!$glossary->usedynalink) {
87
            return;
88
        }
89
        self::reset_course_muc($glossary->course);
90
        if ($glossary->globalglossary) {
91
            self::reset_global_muc();
92
        }
93
    }
94
 
95
    /**
96
     * Fetch concepts for given glossaries.
97
     * @param int[] $glossaries
98
     * @return array
99
     */
100
    protected static function fetch_concepts(array $glossaries) {
101
        global $DB;
102
 
103
        $glossarylist = implode(',', $glossaries);
104
 
105
        $sql = "SELECT id, glossaryid, concept, casesensitive, 0 AS category, fullmatch
106
                  FROM {glossary_entries}
107
                 WHERE glossaryid IN ($glossarylist) AND usedynalink = 1 AND approved = 1
108
 
109
                 UNION
110
 
111
                SELECT id, glossaryid, name AS concept, 1 AS casesensitive, 1 AS category, 1 AS fullmatch
112
                  FROM {glossary_categories}
113
                 WHERE glossaryid IN ($glossarylist) AND usedynalink = 1
114
 
115
                UNION
116
 
117
                SELECT ge.id, ge.glossaryid, ga.alias AS concept, ge.casesensitive, 0 AS category, ge.fullmatch
118
                  FROM {glossary_alias} ga
119
                  JOIN {glossary_entries} ge ON (ga.entryid = ge.id)
120
                 WHERE ge.glossaryid IN ($glossarylist) AND ge.usedynalink = 1 AND ge.approved = 1";
121
 
122
        $concepts = array();
123
        $rs = $DB->get_recordset_sql($sql);
124
        foreach ($rs as $concept) {
125
            $currentconcept = trim(strip_tags($concept->concept));
126
 
127
            // Concept must be HTML-escaped, so do the same as format_string to turn ampersands into &amp;.
128
            $currentconcept = replace_ampersands_not_followed_by_entity($currentconcept);
129
 
130
            if (empty($currentconcept)) {
131
                continue;
132
            }
133
 
134
            // Rule out any small integers, see MDL-1446.
135
            if (is_number($currentconcept) and $currentconcept < 1000) {
136
                continue;
137
            }
138
 
139
            $concept->concept = $currentconcept;
140
 
141
            $concepts[$concept->glossaryid][] = $concept;
142
        }
143
        $rs->close();
144
 
145
        return $concepts;
146
    }
147
 
148
    /**
149
     * Get all linked concepts from course.
150
     * @param int $courseid
151
     * @return array
152
     */
153
    protected static function get_course_concepts($courseid) {
154
        global $DB;
155
 
156
        if (empty($courseid)) {
157
            return array(array(), array());
158
        }
159
 
160
        $courseid = (int)$courseid;
161
 
162
        // Get info on any glossaries in this course.
163
        $modinfo = get_fast_modinfo($courseid);
164
        $cminfos = $modinfo->get_instances_of('glossary');
165
        if (!$cminfos) {
166
            // No glossaries in this course, so don't do any work.
167
            return array(array(), array());
168
        }
169
 
170
        $cache = \cache::make('mod_glossary', 'concepts');
171
        $data = $cache->get($courseid);
172
        if (is_array($data)) {
173
            list($glossaries, $allconcepts) = $data;
174
 
175
        } else {
176
            // Find all course glossaries.
177
            $sql = "SELECT g.id, g.name
178
                      FROM {glossary} g
179
                      JOIN {course_modules} cm ON (cm.instance = g.id)
180
                      JOIN {modules} m ON (m.name = 'glossary' AND m.id = cm.module)
181
                     WHERE g.usedynalink = 1 AND g.course = :course AND cm.visible = 1 AND m.visible = 1
182
                  ORDER BY g.globalglossary, g.id";
183
            $glossaries = $DB->get_records_sql_menu($sql, array('course' => $courseid));
184
            if (!$glossaries) {
185
                $data = array(array(), array());
186
                $cache->set($courseid, $data);
187
                return $data;
188
            }
189
            foreach ($glossaries as $id => $name) {
190
                $name = str_replace(':', '-', $name);
191
                $glossaries[$id] = replace_ampersands_not_followed_by_entity(strip_tags($name));
192
            }
193
 
194
            $allconcepts = self::fetch_concepts(array_keys($glossaries));
195
            foreach ($glossaries as $gid => $unused) {
196
                if (!isset($allconcepts[$gid])) {
197
                    unset($glossaries[$gid]);
198
                }
199
            }
200
            if (!$glossaries) {
201
                // This means there are no interesting concepts in the existing glossaries.
202
                $data = array(array(), array());
203
                $cache->set($courseid, $data);
204
                return $data;
205
            }
206
            $cache->set($courseid, array($glossaries, $allconcepts));
207
        }
208
 
209
        $concepts = $allconcepts;
210
 
211
        // Verify access control to glossary instances.
212
        foreach ($concepts as $modid => $unused) {
213
            if (!isset($cminfos[$modid])) {
214
                // This should not happen.
215
                unset($concepts[$modid]);
216
                unset($glossaries[$modid]);
217
                continue;
218
            }
219
            if (!$cminfos[$modid]->uservisible) {
220
                unset($concepts[$modid]);
221
                unset($glossaries[$modid]);
222
                continue;
223
            }
224
        }
225
 
226
        return array($glossaries, $concepts);
227
    }
228
 
229
    /**
230
     * Get all linked global concepts.
231
     * @return array
232
     */
233
    protected static function get_global_concepts() {
234
        global $DB;
235
 
236
        $cache = \cache::make('mod_glossary', 'concepts');
237
        $data = $cache->get(0);
238
        if (is_array($data)) {
239
            list($glossaries, $allconcepts) = $data;
240
 
241
        } else {
242
            // Find all global glossaries - no access control here.
243
            $sql = "SELECT g.id, g.name
244
                      FROM {glossary} g
245
                      JOIN {course_modules} cm ON (cm.instance = g.id)
246
                      JOIN {modules} m ON (m.name = 'glossary' AND m.id = cm.module)
247
                     WHERE g.usedynalink = 1 AND g.globalglossary = 1 AND cm.visible = 1 AND m.visible = 1
248
                  ORDER BY g.globalglossary, g.id";
249
            $glossaries = $DB->get_records_sql_menu($sql);
250
            if (!$glossaries) {
251
                $data = array(array(), array());
252
                $cache->set(0, $data);
253
                return $data;
254
            }
255
            foreach ($glossaries as $id => $name) {
256
                $name = str_replace(':', '-', $name);
257
                $glossaries[$id] = replace_ampersands_not_followed_by_entity(strip_tags($name));
258
            }
259
            $allconcepts = self::fetch_concepts(array_keys($glossaries));
260
            foreach ($glossaries as $gid => $unused) {
261
                if (!isset($allconcepts[$gid])) {
262
                    unset($glossaries[$gid]);
263
                }
264
            }
265
            $cache->set(0, array($glossaries, $allconcepts));
266
        }
267
 
268
        // NOTE: no access control is here because it would be way too expensive to check access
269
        //       to all courses that contain the global glossaries.
270
        return array($glossaries, $allconcepts);
271
    }
272
 
273
    /**
274
     * Get all concepts that should be linked in the given course.
275
     * @param int $courseid
276
     * @return array with two elements - array of glossaries and concepts for each glossary
277
     */
278
    public static function get_concepts($courseid) {
279
        list($glossaries, $concepts) = self::get_course_concepts($courseid);
280
        list($globalglossaries, $globalconcepts) = self::get_global_concepts();
281
 
282
        foreach ($globalconcepts as $gid => $cs) {
283
            if (!isset($concepts[$gid])) {
284
                $concepts[$gid] = $cs;
285
            }
286
        }
287
        foreach ($globalglossaries as $gid => $name) {
288
            if (!isset($glossaries[$gid])) {
289
                $glossaries[$gid] = $name;
290
            }
291
        }
292
 
293
        return array($glossaries, $concepts);
294
    }
295
}