Failed Conditions
Branch master (3ce7e2)
by Nick
14:43
created

GLOSSARY::delete()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 4
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 8
rs 9.4285
ccs 0
cts 5
cp 0
crap 6
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 26 and the first side effect is on line 24.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
3
/*
4
5
The Glossary item handles:
6
    1. Search matching for particular items.
7
    2. Addition of glossary items
8
    3. Removal of glossary items
9
    4. Notification of pending glossary additions
10
11
Glossary items can only (at present) be added on the Search page,
12
and only in the event that the term has not already been defined.
13
14
[?] will it be possible to amend the term?
15
16
It should not be possible to add a term if no results are found during the search.
17
18
All Glossary items need to be confirmed by a moderator (unless posted by a moderator).
19
As they are being approved/declined they can be modified (spelling etc...).
20
21
*/
22
23
// This handles basic insertion and approval functions for all epobjects
24
include_once INCLUDESPATH."easyparliament/searchengine.php";
1 ignored issue
show
Bug introduced by
The constant INCLUDESPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
25
26
class GLOSSARY {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
27
28
    public $num_terms;			// how many glossary entries do we have
29
                            // (changes depending on how GLOSSARY is called
30
    public $hansard_count;		// how many times does the phrase appear in hansard?
31
    public $query;				// search term
32
    public $glossary_id;		// if this is set then we only have 1 glossary term
33
    public $current_term;		// will only be set if we have a valid epobject_id
34
    public $current_letter;
35
36
    // constructor...
37 8
    public function __construct($args=array()) {
38
    // We can optionally start the glossary with one of several arguments
39
    //		1. glossary_id - treat the glossary as a single term
40
    //		2. glossary_term - search within glossary for a term
41
    // With no argument it will pick up all items.
42
43 8
            $this->db = new ParlDB;
0 ignored issues
show
Bug Best Practice introduced by
The property db does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
44
45 8
            $this->replace_order = array();
0 ignored issues
show
Bug Best Practice introduced by
The property replace_order does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
46 8
            if (isset($args['s']) && ($args['s'] != "")) {
47
                $args['s'] = urldecode($args['s']);
48
                $this->search_glossary($args);
49
            }
50 8
        $got = $this->get_glossary_item($args);
51 8
            if ($got && isset($args['sort']) && ($args['sort'] == 'regexp_replace')) {
52
                // We need to sort the terms in the array by "number of words in term".
53
                // This way, "prime minister" gets dealt with before "minister" when generating glossary links.
54
55
                // sort by number of words
56 8
                foreach ($this->terms as $glossary_id => $term) {
57 8
                    $this->replace_order[$glossary_id] = count(explode(" ", $term['title']));
58 8
                }
59 8
                arsort($this->replace_order);
60
61
                // secondary sort for number of letters?
62
                // pending functionality...
63
64
                // We can either turn off the "current term" completely -
65
                // so that it never links to its own page,
66
                // Or we can handle it in $this->glossarise below
67
                /*
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
68
                if (isset($this->epobject_id)) {
69
                    unset ($this->replace_order[$this->epobject_id]);
70
                }
71
                */
72 8
            }
73
74
            // These stop stupid submissions.
75
            // everything should be lowercase.
76 8
            $this->stopwords = array( "the", "of", "to", "and", "for", "in", "a", "on", "is", "that", "will", "secretary", "are", "ask", "state", "have", "be", "has", "by", "with", "i", "not", "what", "as", "it", "hon", "he", "which", "from", "if", "been", "this", "s", "we", "at", "government", "was", "my", "an", "department", "there", "make", "or", "made", "their", "all", "but", "they", "how", "debate" );
0 ignored issues
show
Bug Best Practice introduced by
The property stopwords does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
77
78 8
    }
79
80 8
    public function get_glossary_item($args=array()) {
81
        // Search for and fetch glossary item with title or glossary_id
82
        // We could also search glossary text that contains the title text, for cross references
83
84 8
        $this->alphabet = array();
0 ignored issues
show
Bug Best Practice introduced by
The property alphabet does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
85 8
        foreach (range ("A", "Z") as $letter) {
86 8
            $this->alphabet[$letter] = array();
87 8
        }
88
89 8
        $q = $this->db->query("SELECT g.glossary_id, g.title, g.body, u.user_id, u.firstname, u.lastname
90
            FROM editqueue AS eq, glossary AS g, users AS u
91
            WHERE g.glossary_id=eq.glossary_id AND u.user_id=eq.user_id AND g.visible=1 AND eq.approved=1
92 8
            ORDER by g.title");
93 8
        if ($q->success() && $q->rows()) {
94 8
            for ($i=0; $i < $q->rows(); $i++) {
95 8
                $this->terms[ $q->field($i,"glossary_id") ] = $q->row($i);
0 ignored issues
show
Bug Best Practice introduced by
The property terms does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
96
                // Now add the epobject to the alphabet navigation.
97 8
                $first_letter = strtoupper(substr($q->field($i,"title"),0,1));
98 8
                $this->alphabet[$first_letter][] = $q->field($i,"glossary_id");
99 8
            }
100
101 8
            $this->num_terms = $q->rows();
102
103
            // If we were given a glossary_id, then we need one term in particular,
104
            // as well as knowing the next and previous terms for the navigation
105 8
            if (isset($args['glossary_id']) && ($args['glossary_id'] != "")) {
106
                $next = 0; $first_term = null;
107
                foreach ($this->terms as $term) {
108
                    if (!$first_term) $first_term = $term;
109
                    $last_term = $term;
110
                    if ($next == 1) {
111
                        $this->next_term = $term;
0 ignored issues
show
Bug Best Practice introduced by
The property next_term does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
112
                        break;
113
                    }
114
                    elseif ($term['glossary_id'] == $args['glossary_id']) {
115
                        $this->glossary_id = $args['glossary_id'];
116
                        $this->current_term = $term;
117
                        $next = 1;
118
119
                    }
120
                    else {
121
                        $this->previous_term = $term;
0 ignored issues
show
Bug Best Practice introduced by
The property previous_term does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
122
                    }
123
                }
124
                // The first term in the list has no previous, so we'll make it the last term
125
                if (!isset($this->previous_term)) {
126
                    $this->previous_term = $last_term;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $last_term seems to be defined by a foreach iteration on line 107. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
127
                }
128
                // and the last has no next, so we'll make it the first
129
                if (!isset($this->next_term)) {
130
                    $this->next_term = $first_term;
131
                }
132
            }
133
134 8
            return ($this->num_terms);
135
        }
136
        else {
137
            return false;
138
        }
139
    }
140
141
    public function search_glossary($args=array()) {
142
        // Search for and fetch glossary item with a title
143
        // Useful for the search page, and nowhere else (so far)
144
145
        $this->query = addslashes($args['s']);
146
        $this->search_matches = array();
0 ignored issues
show
Bug Best Practice introduced by
The property search_matches does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
147
        $this->num_search_matches = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property num_search_matches does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
148
149
        $query = "SELECT g.glossary_id, g.title, g.body, u.user_id, u.firstname, u.lastname
150
            FROM editqueue AS eq, glossary AS g, users AS u
151
            WHERE g.glossary_id=eq.glossary_id AND u.user_id=eq.user_id AND g.visible=1
152
                AND g.title LIKE '%" . $this->query . "%'
153
            ORDER by g.title";
154
        $q = $this->db->query($query);
155
        if ($q->success() && $q->rows()) {
156
            for ($i=0; $i < $q->rows(); $i++) {
157
                $this->search_matches[ $q->field($i,"glossary_id") ] = $q->row($i);
158
            }
159
            $this->num_search_matches = $q->rows();
160
        }
161
    }
162
163
    public function create(&$data) {
164
        // Add a Glossary definition.
165
        // Sets visiblity to 0, and awaits moderator intervention.
166
        // For this we need to start up an epobject of type 2 and then an editqueue item
167
        // where editqueue.epobject_id_l = epobject.epobject_id
168
169
        $EDITQUEUE = new \MySociety\TheyWorkForYou\GlossaryEditQueue;
170
171
        // Assuming that everything is ok, we will need:
172
        // For epobject:
173
        // 		title VARCHAR(255),
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
174
        // 		body TEXT,
175
        // 		type INTEGER,
176
        // 		created DATETIME,
177
        // 		modified DATETIME,
178
        // and for editqueue:
179
        //		edit_id INTEGER PRIMARY KEY NOT NULL,
180
        //		user_id INTEGER,
181
        //		edit_type INTEGER,
182
        //		epobject_id_l INTEGER,
183
        //		title VARCHAR(255),
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
184
        //		body TEXT,
185
        //		submitted DATETIME,
186
        //		editor_id INTEGER,
187
        //		approved BOOLEAN,
188
        //		decided DATETIME
189
190
        global $THEUSER;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
191
192
        if ($data['title'] == '') {
193
            error ("Sorry, you can't define a term without a title");
194
            return false;
195
        }
196
197
        if ($data['body'] == '') {
198
            error ("You haven't entered a definition!");
199
            return false;
200
        }
201
202
        if (is_numeric($THEUSER->user_id())) {
203
            // Flood check - make sure the user hasn't just posted a term recently.
204
            // To help prevent accidental duplicates, among other nasty things.
205
206
            $flood_time_limit = 20; // How many seconds until a user can post again?
207
208
            $q = $this->db->query("SELECT glossary_id
209
                            FROM	editqueue
210
                            WHERE	user_id = '" . $THEUSER->user_id() . "'
211
                            AND		submitted + 0 > NOW() - $flood_time_limit");
212
213
            if ($q->rows() > 0) {
214
                error("Sorry, we limit people to posting one term per $flood_time_limit seconds to help prevent duplicate postings. Please go back and try again, thanks.");
215
                return false;
216
            }
217
        }
218
219
        // OK, let's get on with it...
220
221
        // Tidy up the HTML tags
222
        // (but we don't make URLs into links; only when displaying the comment).
223
        // We can display Glossary terms the same as the comments
224
        $data['title'] = filter_user_input($data['title'], 'comment_title'); // In utility.php
225
        $data['body'] = filter_user_input($data['body'], 'comment'); // In utility.php
226
        // Add the time and the edit type for the editqueue
227
        $data['posted'] = date('Y-m-d H:i:s', time());
228
        $data['edit_type'] = 2;
229
230
        // Add the item to the edit queue
231
        $success = $EDITQUEUE->add($data);
232
233
        if ($success) {
234
            return ($success);
235
        } else {
236
            return false;
237
        }
238
    }
239
240
    public function delete($glossary_id)
241
    {
242
        $q = $this->db->query("DELETE from glossary where glossary_id=$glossary_id LIMIT 1;");
243
        // if that worked, we need to update the editqueue,
244
        // and remove the term from the already generated object list.
245
        if ($q->affected_rows() >= 1) {
246
            unset($this->replace_order[$glossary_id]);
247
            unset($this->terms[$glossary_id]);
248
        }
249
    }
250
251 8
    public function glossarise($body, $tokenize=0, $urlize=0) {
0 ignored issues
show
Unused Code introduced by
The parameter $tokenize is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

251
    public function glossarise($body, /** @scrutinizer ignore-unused */ $tokenize=0, $urlize=0) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
252
    // Turn a body of text into a link-up wonderland of glossary joy
253
254 8
        global $this_page;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
255
256 8
        $findwords = array();
257 8
        $replacewords = array();
258 8
        $URL = new \MySociety\TheyWorkForYou\Url("glossary");
259 8
        $URL->insert(array("gl" => ""));
260
261
        // External links shown within their own definition
262
        // should be the complete and linked url.
263
        // NB. This should only match when $body is a definition beginning with "http:"
264 8
        if (is_string($body) && preg_match("/^(http:*[^\s])$/i", $body)) {
265
            $body = "<a href=\"" . $body . "\" title=\"External link to " . $body . "\">" . $body . "</a>";
266
            return ($body);
267
        }
268
269
        // otherwise, just replace everything.
270
271
        // generate links from URL when wanted
272
        // NB WRANS is already doing this
273 8
        if ($urlize == 1) {
274
            $body = preg_replace("~(http(s)?:\/\/[^\s\n]*)\b(\/)?~i", "<a href=\"\\0\">\\0</a>", $body);
275
        }
276
277
        // check for any glossary terms to replace
278 8
        foreach ($this->replace_order as $glossary_id => $count) {
279 8
            if ($glossary_id == $this->glossary_id)
280 8
                continue;
281
282 8
            $term_body = $this->terms[$glossary_id]['body'];
283 8
            $term_title = $this->terms[$glossary_id]['title'];
284
285 8
            $URL->update(array("gl" => $glossary_id));
286
            # The regex here ensures that the phrase is only matched if it's not already within <a> tags, preventing double-linking. Kudos to http://stackoverflow.com/questions/7798829/php-regular-expression-to-match-keyword-outside-html-tag-a
287 8
            $findwords[$glossary_id] = "/\b(" . $term_title . ")\b(?!(?>[^<]*(?:<(?!\/?a\b)[^<]*)*)<\/a>)/i";
288
            // catch glossary terms within their own definitions
289 8
            if ($glossary_id == $this->glossary_id) {
290
                $replacewords[] = "<strong>\\1</strong>";
291
            }
292
            else {
293 8
                if ($this_page == "admin_glossary") {
294
                    $link_url = "#gl".$glossary_id;
295
                }
296
                else {
297 8
                    $link_url = $URL->generate('url');
298
                }
299 8
                $title = _htmlentities(trim_characters($term_body, 0, 80));
300 8
                $replacewords[] = "<a href=\"$link_url\" title=\"$title\" class=\"glossary\">\\1</a>";
301
            }
302 8
        }
303
        // Highlight all occurrences of another glossary term in the definition.
304 8
        $body = preg_replace($findwords, $replacewords, $body, 1);
305 8
        if (isset($this->glossary_id))
306 8
            $body = preg_replace("/(?<![>\.\'\/])\b(" . $this->terms[$this->glossary_id]['title'] . ")\b(?![<\'])/i", '<strong>\\1</strong>', $body, 1);
307
308
        // Replace any phrases in wikipedia
309
        // TODO: Merge this code into above, so our gloss and wikipedia
310
        // don't clash (e.g. URLs getting doubly munged etc.)
311 8
        $body = \MySociety\TheyWorkForYou\Utility\Wikipedia::wikipedize($body);
312
313 8
        return ($body);
314
    }
315
316
}
317