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
 * Search area base class for areas working at module level.
19
 *
20
 * @package    core_search
21
 * @copyright  2015 David Monllao {@link http://www.davidmonllao.com}
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23
 */
24
 
25
namespace core_search;
26
 
27
defined('MOODLE_INTERNAL') || die();
28
 
29
/**
30
 * Base implementation for search areas working at module level.
31
 *
32
 * Even if the search area works at multiple levels, if module is one of these levels
33
 * it should extend this class, as this class provides helper methods for module level search management.
34
 *
35
 * @package    core_search
36
 * @copyright  2015 David Monllao {@link http://www.davidmonllao.com}
37
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
38
 */
39
abstract class base_mod extends base {
40
 
41
    /**
42
     * The context levels the search area is working on.
43
     *
44
     * This can be overwriten by the search area if it works at multiple
45
     * levels.
46
     *
47
     * @var array
48
     */
49
    protected static $levels = [CONTEXT_MODULE];
50
 
51
    /**
52
     * Returns the module name.
53
     *
54
     * @return string
55
     */
56
    protected function get_module_name() {
57
        return substr($this->componentname, 4);
58
    }
59
 
60
    /**
61
     * Gets the course module for the required instanceid + modulename.
62
     *
63
     * The returned data depends on the logged user, when calling this through
64
     * self::get_document the admin user is used so everything would be returned.
65
     *
66
     * No need more internal caching here, modinfo is already cached.
67
     *
68
     * @throws \dml_missing_record_exception
69
     * @param string $modulename The module name
70
     * @param int $instanceid Module instance id (depends on the module)
71
     * @param int $courseid Helps speeding up things
72
     * @return \cm_info
73
     */
74
    protected function get_cm($modulename, $instanceid, $courseid) {
75
        $modinfo = get_fast_modinfo($courseid);
76
 
77
        // Hopefully not many, they are indexed by cmid.
78
        $instances = $modinfo->get_instances_of($modulename);
79
        foreach ($instances as $cminfo) {
80
            if ($cminfo->instance == $instanceid) {
81
                return $cminfo;
82
            }
83
        }
84
 
85
        // Nothing found.
86
        throw new \dml_missing_record_exception($modulename);
87
    }
88
 
89
    /**
90
     * Helper function that gets SQL useful for restricting a search query given a passed-in
91
     * context.
92
     *
93
     * The SQL returned will be zero or more JOIN statements, surrounded by whitespace, which act
94
     * as restrictions on the query based on the rows in a module table.
95
     *
96
     * You can pass in a null or system context, which will both return an empty string and no
97
     * params.
98
     *
99
     * Returns an array with two nulls if there can be no results for the activity within this
100
     * context (e.g. it is a block context).
101
     *
102
     * If named parameters are used, these will be named gcrs0, gcrs1, etc. The table aliases used
103
     * in SQL also all begin with gcrs, to avoid conflicts.
104
     *
105
     * @param \context|null $context Context to restrict the query
106
     * @param string $modname Name of module e.g. 'forum'
107
     * @param string $modtable Alias of table containing module id
108
     * @param int $paramtype Type of SQL parameters to use (default question mark)
109
     * @return array Array with SQL and parameters; both null if no need to query
110
     * @throws \coding_exception If called with invalid params
111
     */
112
    protected function get_context_restriction_sql(?\context $context, $modname, $modtable,
113
            $paramtype = SQL_PARAMS_QM) {
114
        global $DB;
115
 
116
        if (!$context) {
117
            return ['', []];
118
        }
119
 
120
        switch ($paramtype) {
121
            case SQL_PARAMS_QM:
122
                $param1 = '?';
123
                $param2 = '?';
124
                $param3 = '?';
125
                $key1 = 0;
126
                $key2 = 1;
127
                $key3 = 2;
128
                break;
129
            case SQL_PARAMS_NAMED:
130
                $param1 = ':gcrs0';
131
                $param2 = ':gcrs1';
132
                $param3 = ':gcrs2';
133
                $key1 = 'gcrs0';
134
                $key2 = 'gcrs1';
135
                $key3 = 'gcrs2';
136
                break;
137
            default:
138
                throw new \coding_exception('Unexpected $paramtype: ' . $paramtype);
139
        }
140
 
141
        $params = [];
142
        switch ($context->contextlevel) {
143
            case CONTEXT_SYSTEM:
144
                $sql = '';
145
                break;
146
 
147
            case CONTEXT_COURSECAT:
148
                // Find all activities of this type within the specified category or any
149
                // sub-category.
150
                $pathmatch = $DB->sql_like('gcrscc2.path', $DB->sql_concat('gcrscc1.path', $param3));
151
                $sql = " JOIN {course_modules} gcrscm ON gcrscm.instance = $modtable.id
152
                              AND gcrscm.module = (SELECT id FROM {modules} WHERE name = $param1)
153
                         JOIN {course} gcrsc ON gcrsc.id = gcrscm.course
154
                         JOIN {course_categories} gcrscc1 ON gcrscc1.id = $param2
155
                         JOIN {course_categories} gcrscc2 ON gcrscc2.id = gcrsc.category AND
156
                              (gcrscc2.id = gcrscc1.id OR $pathmatch) ";
157
                $params[$key1] = $modname;
158
                $params[$key2] = $context->instanceid;
159
                // Note: This param is a bit annoying as it obviously never changes, but sql_like
160
                // throws a debug warning if you pass it anything with quotes in, so it has to be
161
                // a bound parameter.
162
                $params[$key3] = '/%';
163
                break;
164
 
165
            case CONTEXT_COURSE:
166
                // Find all activities of this type within the course.
167
                $sql = " JOIN {course_modules} gcrscm ON gcrscm.instance = $modtable.id
168
                              AND gcrscm.course = $param1
169
                              AND gcrscm.module = (SELECT id FROM {modules} WHERE name = $param2) ";
170
                $params[$key1] = $context->instanceid;
171
                $params[$key2] = $modname;
172
                break;
173
 
174
            case CONTEXT_MODULE:
175
                // Find only the specified activity of this type.
176
                $sql = " JOIN {course_modules} gcrscm ON gcrscm.instance = $modtable.id
177
                              AND gcrscm.id = $param1
178
                              AND gcrscm.module = (SELECT id FROM {modules} WHERE name = $param2) ";
179
                $params[$key1] = $context->instanceid;
180
                $params[$key2] = $modname;
181
                break;
182
 
183
            case CONTEXT_BLOCK:
184
            case CONTEXT_USER:
185
                // These contexts cannot contain any activities, so return null.
186
                return [null, null];
187
 
188
            default:
189
                throw new \coding_exception('Unexpected contextlevel: ' . $context->contextlevel);
190
        }
191
 
192
        return [$sql, $params];
193
    }
194
 
195
    /**
196
     * This can be used in subclasses to change ordering within the get_contexts_to_reindex
197
     * function.
198
     *
199
     * It returns 2 values:
200
     * - Extra SQL joins (tables course_modules 'cm' and context 'x' already exist).
201
     * - An ORDER BY value which must use aggregate functions, by default 'MAX(cm.added) DESC'.
202
     *
203
     * Note the query already includes a GROUP BY on the context fields, so if your joins result
204
     * in multiple rows, you can use aggregate functions in the ORDER BY. See forum for an example.
205
     *
206
     * @return string[] Array with 2 elements; extra joins for the query, and ORDER BY value
207
     */
208
    protected function get_contexts_to_reindex_extra_sql() {
209
        return ['', 'MAX(cm.added) DESC'];
210
    }
211
 
212
    /**
213
     * Gets a list of all contexts to reindex when reindexing this search area.
214
     *
215
     * For modules, the default is to return all contexts for modules of that type, in order of
216
     * time added (most recent first).
217
     *
218
     * @return \Iterator Iterator of contexts to reindex
219
     * @throws \moodle_exception If any DB error
220
     */
221
    public function get_contexts_to_reindex() {
222
        global $DB;
223
 
224
        list ($extrajoins, $dborder) = $this->get_contexts_to_reindex_extra_sql();
225
        $contexts = [];
226
        $selectcolumns = \context_helper::get_preload_record_columns_sql('x');
227
        $groupbycolumns = '';
228
        foreach (\context_helper::get_preload_record_columns('x') as $column => $thing) {
229
            if ($groupbycolumns !== '') {
230
                $groupbycolumns .= ',';
231
            }
232
            $groupbycolumns .= $column;
233
        }
234
        $rs = $DB->get_recordset_sql("
235
                SELECT $selectcolumns
236
                  FROM {course_modules} cm
237
                  JOIN {context} x ON x.instanceid = cm.id AND x.contextlevel = ?
238
                       $extrajoins
239
                 WHERE cm.module = (SELECT id FROM {modules} WHERE name = ?)
240
              GROUP BY $groupbycolumns
241
              ORDER BY $dborder", [CONTEXT_MODULE, $this->get_module_name()]);
242
        return new \core\dml\recordset_walk($rs, function($rec) {
243
            $id = $rec->ctxid;
244
            \context_helper::preload_from_record($rec);
245
            return \context::instance_by_id($id);
246
        });
247
    }
248
 
249
    /**
250
     * Indicates whether this search area may restrict access by group.
251
     *
252
     * This should return true if the search area (sometimes) sets the 'groupid' schema field, and
253
     * false if it never sets that field.
254
     *
255
     * (If this function returns false, but the field is set, then results may be restricted
256
     * unintentionally.)
257
     *
258
     * If this returns true, the search engine will automatically apply group restrictions in some
259
     * cases (by default, where a module is configured to use separate groups). See function
260
     * restrict_cm_access_by_group().
261
     *
262
     * @return bool
263
     */
264
    public function supports_group_restriction() {
265
        return false;
266
    }
267
 
268
    /**
269
     * Checks whether the content of this search area should be restricted by group for a
270
     * specific module. Called at query time.
271
     *
272
     * The default behaviour simply checks if the effective group mode is SEPARATEGROUPS, which
273
     * is probably correct for most cases.
274
     *
275
     * If restricted by group, the search query will (where supported by the engine) filter out
276
     * results for groups the user does not belong to, unless the user has 'access all groups'
277
     * for the activity. This affects only documents which set the 'groupid' field; results with no
278
     * groupid will not be restricted.
279
     *
280
     * Even if you return true to this function, you may still need to do group access checks in
281
     * check_access, because the search engine may not support group restrictions.
282
     *
283
     * @param \cm_info $cm
284
     * @return bool True to restrict by group
285
     */
286
    public function restrict_cm_access_by_group(\cm_info $cm) {
287
        return $cm->effectivegroupmode == SEPARATEGROUPS;
288
    }
289
 
290
    /**
291
     * Returns an icon instance for the document.
292
     *
293
     * @param \core_search\document $doc
294
     * @return \core_search\document_icon
295
     */
296
    public function get_doc_icon(document $doc): document_icon {
297
        return new document_icon('monologo', $this->get_module_name());
298
    }
299
 
300
    /**
301
     * Returns a list of category names associated with the area.
302
     *
303
     * @return array
304
     */
305
    public function get_category_names() {
306
        return [manager::SEARCH_AREA_CATEGORY_COURSE_CONTENT];
307
    }
308
}