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 * Blog tags block.
19 *
20 * @package    block_blog_tags
21 * @copyright  2006 Shane Elliott
22 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24
25defined('MOODLE_INTERNAL') || die();
26
27define('BLOCK_BLOG_TAGS_DEFAULTTIMEWITHIN', 90);
28define('BLOCK_BLOG_TAGS_DEFAULTNUMBEROFTAGS', 20);
29define('BLOCK_BLOG_TAGS_DEFAULTSORT', 'name');
30
31class block_blog_tags extends block_base {
32    function init() {
33        $this->title = get_string('pluginname', 'block_blog_tags');
34    }
35
36    function instance_allow_multiple() {
37        return true;
38    }
39
40    function has_config() {
41        return false;
42    }
43
44    function applicable_formats() {
45        return array('all' => true, 'my' => false, 'tag' => false);
46    }
47
48    function instance_allow_config() {
49        return true;
50    }
51
52    function specialization() {
53
54        // load userdefined title and make sure it's never empty
55        if (empty($this->config->title)) {
56            $this->title = get_string('pluginname', 'block_blog_tags');
57        } else {
58            $this->title = $this->config->title;
59        }
60    }
61
62    function get_content() {
63        global $CFG, $SITE, $USER, $DB, $OUTPUT;
64
65        if ($this->content !== NULL) {
66            return $this->content;
67        }
68
69        // make sure blog and tags are actually enabled
70        if (empty($CFG->bloglevel)) {
71            $this->content = new stdClass();
72            $this->content->text = '';
73            if ($this->page->user_is_editing()) {
74                $this->content->text = get_string('blogdisable', 'blog');
75            }
76            return $this->content;
77
78        } else if (!core_tag_tag::is_enabled('core', 'post')) {
79            $this->content = new stdClass();
80            $this->content->text = '';
81            if ($this->page->user_is_editing()) {
82                $this->content->text = get_string('tagsaredisabled', 'tag');
83            }
84            return $this->content;
85
86        } else if ($CFG->bloglevel < BLOG_GLOBAL_LEVEL and (!isloggedin() or isguestuser())) {
87            $this->content = new stdClass();
88            $this->content->text = '';
89            return $this->content;
90        }
91
92        // require the libs and do the work
93        require_once($CFG->dirroot .'/blog/lib.php');
94
95        if (empty($this->config)) {
96            $this->config = new stdClass();
97        }
98
99        if (empty($this->config->timewithin)) {
100            $this->config->timewithin = BLOCK_BLOG_TAGS_DEFAULTTIMEWITHIN;
101        }
102        if (empty($this->config->numberoftags)) {
103            $this->config->numberoftags = BLOCK_BLOG_TAGS_DEFAULTNUMBEROFTAGS;
104        }
105        if (empty($this->config->sort)) {
106            $this->config->sort = BLOCK_BLOG_TAGS_DEFAULTSORT;
107        }
108
109        $this->content = new stdClass();
110        $this->content->text = '';
111        $this->content->footer = '';
112
113        /// Get a list of tags
114        $timewithin = time() - $this->config->timewithin * 24 * 60 * 60; /// convert to seconds
115
116        $context = $this->page->context;
117
118        // admins should be able to read all tags
119        $type = '';
120        if (!has_capability('moodle/user:readuserblogs', context_system::instance())) {
121            $type = " AND (p.publishstate = 'site' or p.publishstate='public')";
122        }
123
124        $sql  = "SELECT t.id, t.isstandard, t.rawname, t.name, COUNT(DISTINCT ti.id) AS ct
125                   FROM {tag} t, {tag_instance} ti, {post} p, {blog_association} ba
126                  WHERE t.id = ti.tagid AND p.id = ti.itemid
127                        $type
128                        AND ti.itemtype = 'post'
129                        AND ti.component = 'core'
130                        AND ti.timemodified > $timewithin";
131
132        if ($context->contextlevel == CONTEXT_MODULE) {
133            $sql .= " AND ba.contextid = $context->id AND p.id = ba.blogid ";
134        } else if ($context->contextlevel == CONTEXT_COURSE) {
135            $sql .= " AND ba.contextid = $context->id AND p.id = ba.blogid ";
136        }
137
138        $sql .= "
139               GROUP BY t.id, t.isstandard, t.name, t.rawname
140               ORDER BY ct DESC, t.name ASC";
141
142        if ($tags = $DB->get_records_sql($sql, null, 0, $this->config->numberoftags)) {
143
144        /// There are 2 things to do:
145        /// 1. tags with the same count should have the same size class
146        /// 2. however many tags we have should be spread evenly over the
147        ///    20 size classes
148
149            $totaltags  = count($tags);
150            $currenttag = 0;
151
152            $size = 20;
153            $lasttagct = -1;
154
155            $etags = array();
156            foreach ($tags as $tag) {
157
158                $currenttag++;
159
160                if ($currenttag == 1) {
161                    $lasttagct = $tag->ct;
162                    $size = 20;
163                } else if ($tag->ct != $lasttagct) {
164                    $lasttagct = $tag->ct;
165                    $size = 20 - ( (int)((($currenttag - 1) / $totaltags) * 20) );
166                }
167
168                $tag->class = ($tag->isstandard ? "standardtag " : "") . "s$size";
169                $etags[] = $tag;
170
171            }
172
173        /// Now we sort the tag display order
174            $CFG->tagsort = $this->config->sort;
175            usort($etags, "block_blog_tags_sort");
176
177        /// Finally we create the output
178        /// Accessibility: markup as a list.
179            $this->content->text .= "\n<ul class='inline-list'>\n";
180            foreach ($etags as $tag) {
181                $blogurl = new moodle_url('/blog/index.php');
182
183                switch ($CFG->bloglevel) {
184                    case BLOG_USER_LEVEL:
185                        $blogurl->param('userid', $USER->id);
186                    break;
187
188                    default:
189                        if ($context->contextlevel == CONTEXT_MODULE) {
190                            $blogurl->param('modid', $context->instanceid);
191                        } else if ($context->contextlevel == CONTEXT_COURSE) {
192                            $blogurl->param('courseid', $context->instanceid);
193                        }
194
195                    break;
196                }
197
198                $blogurl->param('tagid', $tag->id);
199                $link = html_writer::link($blogurl, core_tag_tag::make_display_name($tag),
200                        array('class' => $tag->class,
201                            'title' => get_string('numberofentries', 'blog', $tag->ct)));
202                $this->content->text .= '<li>' . $link . '</li> ';
203            }
204            $this->content->text .= "\n</ul>\n";
205
206        }
207        return $this->content;
208    }
209
210    /**
211     * Return the plugin config settings for external functions.
212     *
213     * @return stdClass the configs for both the block instance and plugin
214     * @since Moodle 3.8
215     */
216    public function get_config_for_external() {
217        // Return all settings for all users since it is safe (no private keys, etc..).
218        $configs = !empty($this->config) ? $this->config : new stdClass();
219
220        return (object) [
221            'instance' => $configs,
222            'plugin' => new stdClass(),
223        ];
224    }
225}
226
227function block_blog_tags_sort($a, $b) {
228    global $CFG;
229
230    if (empty($CFG->tagsort)) {
231        return 0;
232    } else {
233        $tagsort = $CFG->tagsort;
234    }
235
236    if (is_numeric($a->$tagsort)) {
237        return (($a->$tagsort == $b->$tagsort) ? 0 : ($a->$tagsort > $b->$tagsort)) ? 1 : -1;
238    } elseif (is_string($a->$tagsort)) {
239        return strcmp($a->$tagsort, $b->$tagsort); //TODO: this is not compatible with UTF-8!!
240    } else {
241        return 0;
242    }
243}
244
245
246